Skip to content

Expose container exit code and exit time via inspect#1562

Open
bilby91 wants to merge 1 commit into
apple:mainfrom
crunchloop:fix/inspect-exit-code
Open

Expose container exit code and exit time via inspect#1562
bilby91 wants to merge 1 commit into
apple:mainfrom
crunchloop:fix/inspect-exit-code

Conversation

@bilby91
Copy link
Copy Markdown

@bilby91 bilby91 commented May 15, 2026

Summary

ContainerSnapshot currently drops the exit status of the init process, so container inspect and container list cannot tell callers whether a stopped container exited cleanly or with an error. The daemon already has the value (it observes ExitStatus in handleContainerExit); this PR just plumbs it through to the snapshot type and the CLI projection.

Fixes #1501.

Changes

  • ContainerSnapshot gains two optional fields:
    • exitCode: Int32? — exit code of the init process
    • exitedDate: Date? — when the container transitioned to stopped
  • ContainersService.handleContainerExit populates both fields before calling setContainerState.
  • PrintableContainer (the CLI's Codable projection used by inspect / list) carries the new fields through.

Both fields default to nil, so:

  • Snapshots produced by an older daemon decode unchanged (covered by a back-compat test).
  • A newer CLI talking to an unmodified daemon sees nil and JSONEncoder strips the keys from output — no breaking change for downstream consumers parsing today's JSON.

Motivation

We're building crunchloop/devcontainer — an open-source Go runtime for Dev Containers — and are adding apple/container as a first-class backend alongside Docker. Part of that work is a native implementation of the compose subset Dev Containers relies on for multi-service workspaces.

Two concrete things break today without exit-code visibility:

  1. depends_on: condition: service_completed_successfully — a one-shot service (DB seeder, migration, fixture loader) has to finish and exit cleanly before dependents start. With no exit code in inspect, we can only observe "stopped" and have to guess at success.
  2. CI / automation flows driving one-shot containers (lint, test, build steps) need to react to the exit code without scraping container logs.

This is a small plumbing change — the daemon already captures ExitStatus, it just isn't propagated. It's also the kind of low-level primitive that helps anyone building an orchestrator or higher-level tooling on top of apple/container, not only us.

Test plan

  • swift build (full project, 258 modules) — clean
  • swift test --filter ContainerSnapshotTests — 3/3 pass
    • Legacy snapshot JSON decodes with nil exit fields (back-compat)
    • Snapshot round-trips through JSON with the new fields populated
    • Non-zero exit codes preserved across Int32 range (incl. negative, Int32.min, Int32.max)
  • New CLI binary against an unmodified running daemon (0.12.3): runs cleanly, new fields omitted from output (Swift JSONEncoder strips nil optionals)
  • make fmt — no changes

The container daemon already captures `ExitStatus` when the init process
terminates, but the value never reaches `ContainerSnapshot` and is therefore
absent from `container inspect` / `container list` output. This makes it
impossible for callers to distinguish successful exits from failures, or to
implement compose-style `depends_on: condition: service_completed_successfully`
on top of the API.

Add two optional fields to `ContainerSnapshot`:
- `exitCode: Int32?`  — the exit status of the init process
- `exitedDate: Date?` — when the container transitioned to stopped

Both default to nil, so existing snapshot JSON decodes unchanged
(verified by `ContainerSnapshotTests`). The fields are populated in
`ContainersService.handleContainerExit` and surfaced through the CLI's
`PrintableContainer` projection.

Fixes apple#1501
@jglogan
Copy link
Copy Markdown
Contributor

jglogan commented May 18, 2026

Hi @bilby91, could you have a look at this and #1503 which was submitted shortly after #1501 was filed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Request]: Surface lastExitCode on ContainerSnapshot

2 participants