Skip to main content

Contract-driven ECS deploys: render a task definition from SSM, register it, repoint the service, observe the rollout

Project description

longshore

Contract-driven ECS deploys. longshore reads a service's configuration from SSM Parameter Store (/services/{name}/*), renders a Fargate task definition, registers it, repoints the ECS service, and observes the rollout. It is stateless and deliberately small: your infrastructure tooling (Terraform, CloudFormation, CDK, a script) owns the ECS service; longshore owns only the task definition.

Producer (your IaC)                 longshore
───────────────────                 ─────────
owns the ECS service                renders task-def JSON from SSM
owns autoscaling / desired count    registers new task-def revision
owns networking, LB, roles, logs    update_service(taskDefinition=…) — nothing else
owns circuit breaker + rollback     polls rolloutState, reports events
writes /services/{name}/* (SSM)  ─► reads /services/{name}/*

Because update_service is called with only the taskDefinition argument, the tool structurally cannot touch desired count, networking, or load-balancer settings. Rollback is server-side via the ECS deployment circuit breaker — longshore only observes and reports.

Install

uvx longshore --help          # zero-install, always the latest release
uvx longshore==0.1.0 --help   # pinned (recommended in CI)
# or: pip install longshore

Usage

# The main path (CI and local; identical behavior)
longshore deploy --service myapp-production --image <ecr-url>:<tag> [--region us-east-2] [--wait-minutes 12]

# Config gates (read-only against AWS)
longshore validate --service myapp-production                 # contract compliance report
longshore render --service myapp-production [--image <url>] [--output task-def.json]
longshore diff   --service myapp-production [--image <url>]   # vs the registered revision

# Operator verbs (need broader IAM than the deploy path; see below)
longshore run  --service myapp-production --command 'php artisan migrate --force'
longshore exec --service myapp-production                     # needs the session-manager-plugin

Cluster, roles, networking, and log-group coordinates all come from SSM — there is no per-service config file. Adding a service or a secret requires no change here.

Exit codes

  • 0 — success (deploy: rollout COMPLETED; run: container exited 0).
  • non-zero — validation error, rollout FAILED (circuit breaker triggered; rollback is already underway server-side), watch timeout, or the one-off task's exit code.

The contract

The SSM interface — which parameter keys the tool understands relative to /services/{name}/, their parse semantics, and when they are required — is defined by the versioned contract bundled with the package: src/longshore/data/services.yaml. It is the single source of truth and is not user-configurable; any producer that writes compliant parameters works with this tool unmodified.

longshore validate --service <name> reports a live prefix's compliance: missing required keys, type/enum violations, structural problems (e.g. a sidecar without a name), per-container env counts, and any keys under the prefix the tool ignores.

Highlights of the interface:

  • Env is per-container and secrets-only. Every parameter under containers/{container}/env/ (String or SecureString) becomes a valueFrom secret on that container — values are never read by the tool, only ARNs; ECS resolves them at task start via the execution role.
  • Sidecars are raw container definitions published at ecs/sidecars with producer-pinned images; --image only ever targets the primary container.
  • FireLens needs no special support: a log-router sidecar plus an ecs/log_configuration override (used verbatim) switches the primary container to awsfirelens.

Interface changes bump the contract version and ship as new releases.

GitHub Action

The repo doubles as a composite action pinned to the same version as the code. Pin it by a release's full commit SHA (release tags are immutable, but a SHA pin is verifiable and Dependabot keeps it current):

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # OIDC
      contents: read
    steps:
      - uses: telasystems/longshore@912e504669e94336612a28e50870d032badc075e # v0.1.0
        with:
          service-name: myapp-production
          image: ${{ needs.build.outputs.image }}
          role-arn: ${{ vars.DEPLOY_ROLE_ARN }}
          aws-region: us-east-2

Each release's SHA is listed on the releases page; no floating v0/v1 tag is maintained.

IAM for the deploy path

A CI deploy role needs only: ssm:GetParameter/GetParametersByPath on the service prefix, ecs:RegisterTaskDefinition (+ DescribeTaskDefinition, TagResource), ecs:UpdateService/DescribeServices on the one service, iam:PassRole on the task's two roles, and (optional, for failure diagnostics) cluster-scoped ecs:ListTasks/DescribeTasks. run and exec intentionally need more (ecs:RunTask, ecs:ExecuteCommand) — keep those off the CI role and use operator credentials.

Development

uv sync                      # install (incl. dev deps)
uv run pytest                # tests (renderer, contract pinning, engine, CLI)
uv run ruff check .          # lint
uv run ruff format --check . # formatting
uv run mypy                  # types

The contract-pinning tests walk the bundled contract and assert the renderer honors every entry (type, default, required, enum) — a contract change fails tests until the renderer handles it, so spec and code cannot drift silently.

License

MIT © Tela Systems

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

longshore-0.1.1.tar.gz (83.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

longshore-0.1.1-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

Details for the file longshore-0.1.1.tar.gz.

File metadata

  • Download URL: longshore-0.1.1.tar.gz
  • Upload date:
  • Size: 83.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for longshore-0.1.1.tar.gz
Algorithm Hash digest
SHA256 feb2941651c057fc9333acf68471bd89c62d40a67734f3b72045b2e425d9ef64
MD5 40565305e0fedac2adf80168395af680
BLAKE2b-256 810d1bfcb114af7b700deffe8df6218c6e577055eefd3e3995cb8fa92eb22823

See more details on using hashes here.

Provenance

The following attestation bundles were made for longshore-0.1.1.tar.gz:

Publisher: release.yml on telasystems/longshore

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file longshore-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: longshore-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for longshore-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2f48968fe5ed1cbcb869df260380396e19878ca29f384388ad3112fd12a509b0
MD5 77255fedacc53b84f8a85f352e3dc684
BLAKE2b-256 9b969c640fa62030a39b7a362293c2e5af8500008ec6b3745d9b6c7abc3e7796

See more details on using hashes here.

Provenance

The following attestation bundles were made for longshore-0.1.1-py3-none-any.whl:

Publisher: release.yml on telasystems/longshore

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page