ecspresso

The atago project wrote these specs on its own initiative and runs them in its own CI, to exercise atago against a real program. They are not ecspresso’s official test suite, and the ecspresso project is not affiliated with atago.

Summary #

2 suites · 8 scenarios

Contents #

ecspresso + deploy (real ECS deploy against a mock) #

The thing ecspresso exists to do: deploy an ECS service, and roll it when the definition changes. This suite runs that for real against a mock AWS endpoint (moto, an in-memory AWS mock), so no AWS account is involved and nothing is billed.

The deploy is verified from the outside. Rather than trusting ecspresso’s own log output, the resulting ECS state is read back with aws ecs describe-services — a second, independent client — so the assertion is about what the service actually looks like afterwards.

Source: test/e2e/thirdparty/ecspresso/deploy.atago.yaml

Scenario: deploy registers the task definition, creates the service, and rolls it #

only when command -v ecspresso && command -v moto_server && command -v aws succeeds

Given #

  • Background service moto is started: moto_server -H 127.0.0.1 -p 15111.
  • Fixture file ecspresso.yml is created.
  • Fixture file taskdef.json is created.
  • Fixture file servicedef.json is created.

Inputs #

Fixture ecspresso.yml:

region: us-east-1
cluster: demo
service: web
task_definition: taskdef.json
service_definition: servicedef.json

Fixture taskdef.json:

{
  "family": "web",
  "containerDefinitions": [
    { "name": "web", "image": "nginx:1.0", "cpu": 128, "memory": 256, "essential": true }
  ]
}

Fixture servicedef.json:

{ "launchType": "EC2", "desiredCount": 2, "schedulingStrategy": "REPLICA" }

When #

aws --endpoint-url http://127.0.0.1:15111 ecs create-cluster --cluster-name demo
ecspresso deploy --config ecspresso.yml --no-wait
aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].status" --output text
aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].taskDefinition" --output text
aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].desiredCount" --output text
sed 's/nginx:1.0/nginx:2.0/' taskdef.json > taskdef.new && mv taskdef.new taskdef.json
ecspresso deploy --config ecspresso.yml --no-wait
aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].taskDefinition" --output text
aws --endpoint-url http://127.0.0.1:15111 ecs list-task-definitions --query "length(taskDefinitionArns)" --output text

Then #

  • after aws --endpoint-url http://127.0.0.1:15111 ecs create-cluster --cluster-name demo:
    • exit code is 0
  • after ecspresso deploy --config ecspresso.yml --no-wait:
    • exit code is 0
  • after aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].status" --output text:
    • exit code is 0
    • stdout contains ACTIVE
  • after aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].taskDefinition" --output text:
    • exit code is 0
    • stdout contains web:1
  • after aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].desiredCount" --output text:
    • exit code is 0
    • stdout contains 2
  • after sed 's/nginx:1.0/nginx:2.0/' taskdef.json > taskdef.new && mv taskdef.new taskdef.json:
    • exit code is 0
  • after ecspresso deploy --config ecspresso.yml --no-wait:
    • exit code is 0
  • after aws --endpoint-url http://127.0.0.1:15111 ecs describe-services --cluster demo --services web --query "services[0].taskDefinition" --output text:
    • exit code is 0
    • stdout contains web:2
  • after aws --endpoint-url http://127.0.0.1:15111 ecs list-task-definitions --query "length(taskDefinitionArns)" --output text:
    • exit code is 0
    • stdout contains 2

ecspresso (Amazon ECS deploy tool) #

ecspresso deploys ECS services, which needs AWS — but everything that decides what gets deployed happens locally first. ecspresso render evaluates a task and service definition without touching a cluster, and that is the surface pinned here: the step where a mistake becomes a bad deploy.

What is guaranteed: template functions expand to the documented values and fall back to their defaults when unset; jsonnet definitions resolve with external variables supplied; config paths resolve as documented; and the failure modes stay loud and distinct — an undefined must_env and a missing config file each fail with their own exit code rather than rendering something half-substituted.

The rendered task definition is asserted as structured JSON, so a reformatting change cannot break the suite while a semantic change slips through.

Source: test/e2e/thirdparty/ecspresso/ecspresso.atago.yaml

Scenario: version prints without error #

only when ecspresso version succeeds

When #

ecspresso version

Then #

  • exit code is 0

Scenario: render substitutes an env template function #

only when ecspresso version succeeds

Given #

  • Fixture file ecspresso.yml is created.
  • Fixture file taskdef.json is created.
  • Environment variables are set: IMAGE_TAG.

Inputs #

Fixture ecspresso.yml:

region: ap-northeast-1
cluster: demo
service: web
task_definition: taskdef.json

Fixture taskdef.json:

{
  "family": "web",
  "containerDefinitions": [
    {
      "name": "web",
      "image": "nginx:{{ env `IMAGE_TAG` `latest` }}",
      "cpu": 128,
      "memory": 256
    }
  ]
}

When #

ecspresso render --config ecspresso.yml taskdef

Then #

  • exit code is 0
  • stdout at $.containerDefinitions[0].image equals nginx:1.2.3

Scenario: render falls back to the template default when the env is unset #

only when ecspresso version succeeds

Given #

  • Fixture file ecspresso.yml is created.
  • Fixture file taskdef.json is created.

Inputs #

Fixture ecspresso.yml:

region: ap-northeast-1
cluster: demo
service: web
task_definition: taskdef.json

Fixture taskdef.json:

{
  "family": "web",
  "containerDefinitions": [
    {
      "name": "web",
      "image": "nginx:{{ env `IMAGE_TAG` `latest` }}",
      "cpu": 128,
      "memory": 256
    }
  ]
}

When #

ecspresso render --config ecspresso.yml taskdef

Then #

  • exit code is 0
  • stdout at $.containerDefinitions[0].image equals nginx:latest

Scenario: render evaluates a jsonnet task definition with an external variable #

only when ecspresso version succeeds

Given #

  • Fixture file ecspresso.yml is created.
  • Fixture file task.jsonnet is created.

Inputs #

Fixture ecspresso.yml:

region: ap-northeast-1
cluster: demo
service: web
task_definition: task.jsonnet

Fixture task.jsonnet:

{
  family: "web",
  containerDefinitions: [
    {
      name: "web",
      image: "nginx:" + std.extVar("tag"),
      cpu: 64,
      memory: 128,
    },
  ],
}

When #

ecspresso render --config ecspresso.yml --ext-str tag=9.9.9 taskdef

Then #

  • exit code is 0
  • stdout at $.containerDefinitions[0].image equals nginx:9.9.9

Scenario: render config resolves the defaults #

only when ecspresso version succeeds

Given #

  • Fixture file ecspresso.yml is created.
  • Fixture file taskdef.json is created.

Inputs #

Fixture ecspresso.yml:

region: ap-northeast-1
cluster: demo
service: web
task_definition: taskdef.json

Fixture taskdef.json:

{"family": "web", "containerDefinitions": []}

When #

ecspresso render --config ecspresso.yml config

Then #

  • exit code is 0
  • stdout contains cluster: demo, timeout:

Scenario: an undefined must_env fails the render #

only when ecspresso version succeeds

Given #

  • Fixture file ecspresso.yml is created.
  • Fixture file taskdef.json is created.

Inputs #

Fixture ecspresso.yml:

region: ap-northeast-1
cluster: demo
service: web
task_definition: taskdef.json

Fixture taskdef.json:

{
  "family": "web",
  "containerDefinitions": [
    {
      "name": "web",
      "image": "nginx:{{ must_env `REQUIRED_TAG` }}",
      "cpu": 1,
      "memory": 1
    }
  ]
}

When #

ecspresso render --config ecspresso.yml taskdef

Then #

  • exit code is 2
  • stderr contains REQUIRED_TAG is not defined

Scenario: a missing config file fails cleanly #

only when ecspresso version succeeds

When #

ecspresso render --config nonexistent.yml taskdef

Then #

  • exit code is 1
  • stderr contains failed to load config file