fix(docker): handling for exited containers that fail to start#582
Conversation
Signed-off-by: Samuel K <skevetter@pm.me>
✅ Deploy Preview for images-devsy-sh canceled.
|
📝 WalkthroughWalkthroughContainer state model gains ExitCode and Error fields. WaitContainerRunning is refactored to distinguish terminal versus exited containers using new sentinel errors and a grace period, with centralized error/log formatting. Container restart logic adds a bounded retry loop. Corresponding unit tests are added. ChangesContainer wait and restart resilience
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Driver
participant ensureContainerRunning
participant restartAndWait
participant DockerHelper
Driver->>ensureContainerRunning: ensureContainerRunning(ctx)
loop up to containerRestartAttempts
ensureContainerRunning->>ensureContainerRunning: check ctx.Err()
ensureContainerRunning->>restartAndWait: restartAndWait()
restartAndWait->>DockerHelper: StartContainer / WaitContainerRunning
DockerHelper-->>restartAndWait: success or error
restartAndWait-->>ensureContainerRunning: result
alt terminal error
ensureContainerRunning-->>Driver: ErrContainerTerminal
else success
ensureContainerRunning-->>Driver: nil
end
end
ensureContainerRunning-->>Driver: wrapped ErrContainerTerminal after attempts exhausted
sequenceDiagram
participant Caller
participant WaitContainerRunning
participant InspectContainers
participant evaluateContainerState
Caller->>WaitContainerRunning: WaitContainerRunning(ctx)
loop poll until timeout
WaitContainerRunning->>InspectContainers: InspectContainers()
InspectContainers-->>WaitContainerRunning: container state
WaitContainerRunning->>evaluateContainerState: evaluateContainerState(state, elapsed)
evaluateContainerState-->>WaitContainerRunning: ErrContainerTerminal / ErrContainerExited / nil
end
WaitContainerRunning-->>Caller: success or containerStateError
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Deploy Preview for devsydev canceled.
|
Signed-off-by: Samuel K <skevetter@pm.me>
Signed-off-by: Samuel K <skevetter@pm.me>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
pkg/docker/helper.go (2)
33-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winHardcoded wait/grace timing hurts test speed and determinism.
containerRunningPollInterval,containerRunningTimeout, andcontainerExitGraceare unexported package constants with no way to override them in tests. As a result,TestWaitContainerRunning_ExitedFailsFast, and especially the 3-attempt retry tests inlifecycle_test.go(RecoversOnRetry,NeverStartsFailsFast), each burn multiple real wall-clock seconds (containerExitGrace × attempts) just waiting out the grace period, rather than being deterministic and fast.Consider exposing these as (overridable) fields on
DockerHelper(with these constants as defaults) so tests can inject shorter values.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/docker/helper.go` around lines 33 - 37, The hardcoded timing constants in DockerHelper make wait/retry tests slow and non-deterministic. Update DockerHelper to carry overridable timing fields for containerRunningPollInterval, containerRunningTimeout, and containerExitGrace, and keep the current constants as defaults. Then adjust the wait/retry paths in the DockerHelper methods used by WaitContainerRunning and the lifecycle retry flow so tests can inject shorter values instead of burning real wall-clock time.
264-267: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTimeout error loses container-state context when there's no inspect error.
lastErris reset tonilon every successful inspect (line 260). If the container gets stuck in a non-terminal, non-running status (e.g.created/restarting) for the full 30s without any inspect error,pollErrwill be the generic wait-timeout error with no indication of the last observed container status/exit info, making failures harder to diagnose than the terminal/exited paths.Consider tracking the last observed
state/statusalongsidelastErrand including it in the final error whenpollErris a plain timeout.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/docker/helper.go` around lines 264 - 267, The timeout path in the polling loop loses container-state context when `lastErr` is nil, so update the `helper.go` logic around the inspect polling to also remember the last observed container `state`/`status` alongside `lastErr`. Then, in the final `pollErr` handling, detect the plain wait-timeout case and wrap it with the saved state/status details so failures from stuck containers (for example `created` or `restarting`) still report the last known container condition. Keep the existing `pollErr`/`lastErr` behavior for inspect failures, but make the final returned error from the polling function include the captured container-state context when there was no inspect error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@pkg/docker/helper.go`:
- Around line 33-37: The hardcoded timing constants in DockerHelper make
wait/retry tests slow and non-deterministic. Update DockerHelper to carry
overridable timing fields for containerRunningPollInterval,
containerRunningTimeout, and containerExitGrace, and keep the current constants
as defaults. Then adjust the wait/retry paths in the DockerHelper methods used
by WaitContainerRunning and the lifecycle retry flow so tests can inject shorter
values instead of burning real wall-clock time.
- Around line 264-267: The timeout path in the polling loop loses
container-state context when `lastErr` is nil, so update the `helper.go` logic
around the inspect polling to also remember the last observed container
`state`/`status` alongside `lastErr`. Then, in the final `pollErr` handling,
detect the plain wait-timeout case and wrap it with the saved state/status
details so failures from stuck containers (for example `created` or
`restarting`) still report the last known container condition. Keep the existing
`pollErr`/`lastErr` behavior for inspect failures, but make the final returned
error from the polling function include the captured container-state context
when there was no inspect error.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 076fc7cc-5bec-417b-ae0b-c8a99b45b2ae
📒 Files selected for processing (6)
pkg/devcontainer/config/container_details.gopkg/docker/helper.gopkg/docker/helper_test.gopkg/driver/docker/docker_test.gopkg/driver/docker/lifecycle.gopkg/driver/docker/lifecycle_test.go
Signed-off-by: Samuel K skevetter@pm.me
Summary by CodeRabbit
New Features
Bug Fixes
Tests