feat(api): surface lastExitCode on ContainerSnapshot#1503
Open
chrisgeo wants to merge 4 commits into
Open
Conversation
Adds an optional lastExitCode: Int32? field to ContainerSnapshot,
populated in ContainersService.handleContainerExit when the container
transitions to .stopped. The exit code is taken from the existing
ExitMonitor callback's ExitStatus value.
Motivation
----------
External orchestrators that drive the API server (Docker-Compose-style
service ordering is the canonical use case) need to distinguish between
'container exited cleanly' and 'container exited but the host gave up
on it'. The compose-spec depends_on: condition: service_completed_successfully
gate is exactly this distinction. Today, ContainerSnapshot exposes
.stopped without an exit code, so consumers can only fall back to a
stopped-on-time check and silently misinterpret a non-zero exit as
success.
Wire compatibility
------------------
ContainerSnapshot is marshaled as Codable JSON over XPC. Adding an
optional field is forward-compatible:
- Older clients reading from a newer server: ignore the new key.
- Newer clients reading from an older server: decode lastExitCode
as nil (which the doc comment already documents as 'never exited
or exit not captured').
Scope
-----
In-memory only. The exit code lives in the in-memory ContainerState
snapshot for the duration of apiserver uptime. A daemon restart resets
all snapshots to .stopped without exit codes (existing behavior). A
follow-up patch can add bundle persistence (exit_status.json) if the
daemon-restart case becomes operationally relevant.
Files
-----
- Sources/ContainerResource/Container/ContainerSnapshot.swift: new
field + init parameter (default nil for backward compat at all
existing construction sites; SandboxService.swift's snapshot
constructor is unchanged).
- Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift:
one assignment in handleContainerExit's terminal state block, taking
exitCode from the existing 'code: ExitStatus?' parameter.
4 tasks
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Resolve ContainersService exit-state conflict by preserving upstream runtime-client refactor and applying lastExitCode snapshot handling. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion issue: #1501
Type of Change
Motivation and Context
ContainerSnapshottoday exposesRuntimeStatus.stoppedbut not the underlying exit code. External orchestrators, such as a Compose-spec orchestrator implementingdepends_on: condition: service_completed_successfully, need to distinguish clean exit (exit code 0) from failed exit (non-zero) of a one-shot container. Without this, they have to treat.stoppedas success and can silently misinterpret non-zero exits.The data already exists internally:
ContainersService.handleContainerExit(id:code:context:)receivesExitStatus?from the existingExitMonitorcallback wiring when a container exits via the runtime. This PR stamps that value onto the container snapshot.See #1501 for the full motivation and design notes.
What this PR changes
lastExitCode: Int32?toContainerSnapshot, with a defaulted initializer parameter so existing construction sites continue to compile unchanged.ContainersServicehandles a container exit.ContainerSnapshot.lastExitCodedefaulting tonil.lastExitCode.lastExitCode..stopped, stampslastExitCode, and clears network attachments.Wire compatibility
ContainerSnapshotis marshaled asCodableJSON over XPC. This change is an additive optional scalar field, not a replacement of an existing Codable field's type or representation.That distinction matters: replacing an existing persisted
Stringpath field with a synthesizedFilePathfield would change JSON from a plain string such as"/mnt/data"into swift-system's synthesized object representation, which would break persistedRuntimeConfigurationpayloads on upgrade. This PR does not make that kind of wire-format change.For this change:
lastExitCodekey.lastExitCodekey asnil, matching the documented "never exited or exit not captured" case.ContainerSnapshotTestsincludes an explicit missing-key decode test for this compatibility path.Scope
This is intentionally in-memory only. The exit code lives in the in-memory
ContainerStatesnapshot for API-server uptime. A daemon restart resets snapshots to.stoppedwithout exit codes, matching existing restart behavior. Bundle persistence, such as writing anexit_status.json, is a deliberate out-of-scope follow-up.Testing
swift buildswift test --filter 'ContainerSnapshotTests|ContainerExitStatusTests'ContainerSnapshot.lastExitCodefield. No separate docs page appears to describe this model field today.