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 behaviour)
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:

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

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 honours 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.0rc1.tar.gz (80.9 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.0rc1-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file longshore-0.1.0rc1.tar.gz.

File metadata

  • Download URL: longshore-0.1.0rc1.tar.gz
  • Upload date:
  • Size: 80.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for longshore-0.1.0rc1.tar.gz
Algorithm Hash digest
SHA256 b0bb93a7700176d06f5de7d217b2ed63ab276ce032e51399e9a3dac5a72b366d
MD5 a818ac865f8d624e92c4bab6bb61d25b
BLAKE2b-256 c5afc832cee3ff636988427ee37658c98ea1a9e4bc8419a20051542e9d0e2132

See more details on using hashes here.

Provenance

The following attestation bundles were made for longshore-0.1.0rc1.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.0rc1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for longshore-0.1.0rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 a4f2d2b59cbec93fea6464d36a1384fb7e7b5453300e90805a560b8abe25f7fd
MD5 81d4ec09828c25e86da9691ac1e75ed7
BLAKE2b-256 ed66ff982351df3b8810f8d72d4b31229507d701a219d19263902cac5c90b569

See more details on using hashes here.

Provenance

The following attestation bundles were made for longshore-0.1.0rc1-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