Commit c3ab183
feat(container): persistent container component kind + compositions (#2645)
* feat(container): add persistent container component kind + compositions
Add a stack-scoped, Atmos-native `container` component kind: one component is
one persistent service. Atmos owns the image artifact (build/push/pull) and a
long-running named container lifecycle (list/up/ps/logs/exec/restart/stop/rm/down),
discovered by labels derived from the canonical component instance address
(`<stack>/container/<component>`) rather than local state files.
- pkg/container: identity (canonical labels, RuntimeName, DiscoveryFilter) +
named lifecycle (Up/Down/FindInstance), sharing the sanitizer with sandbox.go.
- pkg/component/container: provider + executor for the verbs; first-class config
(`image`/`build`/`run` reuse the workflow container-step structs, NOT `vars`);
build-before-start; abstract components rejected.
- cmd/container + cmd/composition: thin CommandProvider command groups.
- `atmos container list` shows per-instance running state (label discovery). The
generic `atmos list components` lists containers as a component type uniformly,
without container-specific status (consistent with terraform/ansible).
- compositions: first-class `composition:` membership + `compositions:` section;
hard error on undeclared membership; `atmos composition validate` soft report.
- Custom-component fallback now runs metadata.inherits inheritance + generic
deep-merge of all top-level keys, so container honors catalog/abstract defaults.
- describe component auto-detect + describe/list type whitelist extended for
container (and the pre-existing ansible gap in list components).
- Examples (container-component, compositions), docs, JSON schema, and a new
atmos-core-component-development contributor skill.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(container): address review feedback and fix CI failures
Apply CodeRabbit review fixes for PR #2645 (container component type +
compositions) and fix the failing Acceptance Tests.
First-class image/build contract:
- ValidateComponent and its tests check top-level image/build (not vars.*)
- executor error text and verb help/usage docs use first-class keys
Correctness/robustness:
- lifecycle.Up joins the cleanup error when Start fails (no orphan masking)
- ensureImage only builds on a genuine missing image; export and reuse
IsImageMissingError so transport/auth/daemon errors surface
- composition + container resolve stack via viper (flag > env > config)
- container component completion filters by stack and container type
- scope FParseErrWhitelist to `exec` only so other verbs catch typos
- customComponentInheritsBases errors on malformed metadata.inherits
Schema:
- container_component_manifest accepts !include via oneOf
- compositions require non-empty, unique services
- classify ContainerSectionName in the schema-coverage ratchet
Tests/docs:
- assert port-binding values (not just length); add composition executor
unit tests and a negative-path inherits test; docstrings on new exports
- doc fixes (comma, explicit ps/logs/exec commands, Next Steps link)
CI:
- regenerate TestCLICommands/atmos_non-existent snapshot to include the
new composition and container commands
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(container): unify all labels under tools.atmos.* namespace
Replace the two legacy label namespaces (com.cloudposse.atmos.* for
container-component instances and com.atmos.* for workflow sandboxes,
container steps, and devcontainers) with a single tools.atmos.* namespace
so the label vocabulary is consistent repo-wide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(container): bulk lifecycle ops, log tailing, start verb, polish
Add multi-component and interactive operation to the container component
lifecycle, mirroring docker compose, plus several UX fixes.
Bulk operation:
- build/push/pull/up/start/restart/stop/rm/down accept no component and
operate on --all (optionally --stack-scoped) or via an interactive picker
(stack, then multi-select of components). Continue-on-error with an
aggregated summary; teardown verbs (down/stop/rm) run in reverse order.
- Reuse describeStacks + collectContainerInstances for target discovery;
per-target dispatch reuses the existing single-component executors.
logs --all / --follow:
- logs takes an optional component plus --all/--follow/--tail. Multi-follow
streams concurrently with a colored, centered, uppercased per-component
label (log-level-badge style via pkg/ui/theme.ComponentLabelStyle), built
on pkg/io.NewLinePrefixWriterRaw; degrades to [NAME] without color.
- Ctrl-C stops gracefully: suspends Atmos's global interrupt-exit
(pkg/signals) and prints a friendly closing line instead of exiting 130.
start verb:
- Add `start` to resume an existing stopped container in place (inverse of
stop; unlike up it never creates/recreates), restoring up/down + start/stop
symmetry.
ps + list:
- `ps` no longer requires a component; with none it lists all components'
running state (like `list`, optionally --stack-filtered).
- Fix list/ps column alignment: render the colored status dot outside
tabwriter (its ANSI codes inflated the byte-counted column width).
Other:
- Spinners on lifecycle ops (build/push/pull/up/start/restart/stop/rm/down),
matching the devcontainer UX.
- Fix the --stack flag being dropped for single-component invocations by
rebinding the executing command's flags to Viper in runVerb.
- Give --tail a no-opt default so bare `--tail` does not swallow the next flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(container): push every build tag (multi-registry push in one op)
`atmos container push <component>` (and `push --all`) now pushes every entry
in `build.tags` instead of only the single top-level `image`. Because the build
already applies each tag to the image locally, listing registry-qualified tags
in `build.tags` ships the image to multiple registries in one operation. Pushes
run in order and fail fast; with no `build.tags`, push falls back to `image`
(unchanged behavior).
- pkg/component/container/config.go: ContainerSpec.PushRefs() returns the deduped,
order-preserving build tags, else the single image.
- pkg/component/container/executor.go: ExecutePush fans out over PushRefs()
(per-ref spinner, fail-fast, dry-run lists each ref).
- docs: dedicated "Push to multiple registries" section in the container usage
page, plus Usage/Configuration/Examples mentions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(container): first-class healthcheck and restart policies
Add Docker-Compose-style `run.healthcheck` and `run.restart` as first-class
container component properties, so users no longer need the raw `run.run_args`
passthrough. Health status is now surfaced in `atmos container ps`/`list`.
- `restart: { policy, max_retries }` -> docker/podman --restart=<policy>[:<n>]
- `healthcheck:` mirrors Compose (test string/list with NONE|CMD|CMD-SHELL,
interval/timeout/retries/start_period/start_interval/disable) -> --health-*
/ --no-healthcheck
- ps/list gain a HEALTH column, parsed for free from the existing ps .Status
string (no extra runtime call) in both docker and podman paths
- restart policy and healthcheck durations validated up front with friendly
errors instead of opaque runtime failures
- JSON manifest schema, the container-component example, and the usage docs
document the new settings
Adding the Health field to container.Info / instanceRow grew those structs, so
a few existing by-value range/param copies are switched to indexing/pointers to
satisfy gocritic (sandbox.go, podman.go, logs.go).
Plumbing mirrors the existing ports/mounts flow: schema.ContainerRunStep ->
ContainerSpec mappers/ValidateRun -> NamedConfig -> CreateConfig ->
addHealthAndRestart flag emitter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(container): address CodeRabbit review feedback on PR #2645
Apply still-valid CodeRabbit findings; skip the rest with rationale.
Correctness/robustness:
- container: IsContainerRunning tokenizes status (no false match on
"not running"); identity labels are now authoritative over caller
labels in buildNamedCreateConfig (label-based discovery stays intact)
- container: CommandArgs tokenizes run.command with google/shlex so
quoted args survive in ExecuteUp; errors on malformed quoting
- container logs: only "no running container" is skippable
(new ErrNoRunningContainer sentinel); real discovery failures
aggregate and surface; follow errors wrap a static sentinel
- ui: guard negative index in FormatComponentLabel and
ComponentLabelStyle to avoid a negative-modulus panic
- stacks: non-string metadata.inherits items now error instead of
being silently dropped
- schema: compositions accepts !include like peer sections
Tests/docs/cleanup:
- fail-loud fixture check; "not running" regression case; both-direction
label isolation; ShellOptions IO propagation; ShellOptions test names;
table-driven composition arg test; push stub Shell fails loudly;
pin mockgen to v0.6.0; correct stale describe_stacks comment; e.g.,
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(changelog): add container components blog post and roadmap milestone
PR #2645 is labeled `minor`, so the release-docs gate requires a changelog
entry and a roadmap update for the user-facing container component kind +
compositions feature.
- Add website/blog/2026-06-23-container-components.mdx (slug: container-components)
- Mark the "Container components with component-instance lifecycle" and
"Atmos compositions as system boundaries" milestones as shipped in the
Container Composition & Local Development initiative (pr: 2645,
changelog: container-components); recompute progress 29% -> 57%
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>1 parent 2c6912a commit c3ab183
103 files changed
Lines changed: 9003 additions & 217 deletions
File tree
- .claude/skills
- atmos-core-component-development
- agent-skills/skills/atmos-devcontainer
- cmd
- composition
- container
- docs/prd
- errors
- examples
- compositions
- stacks/deploy
- container-component
- stacks/deploy
- internal/exec
- pkg
- component/container
- composition
- config
- container
- datafetcher
- schema/atmos/manifest
- devcontainer
- flags
- io
- list/extract
- runner/step
- schema
- ui
- theme
- tests/snapshots
- website
- blog
- docs
- cli/commands/container
- components
- src/data
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
0 commit comments