cf67ba0a86
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 2m27s
CI / docker (pull_request) Successful in 49s
CI / integration_tests (pull_request) Successful in 3m46s
CI / coverage (pull_request) Successful in 4m56s
CI / lint (push) Successful in 12s
CI / build (push) Successful in 14s
CI / quality (push) Successful in 16s
CI / security (push) Successful in 32s
CI / typecheck (push) Successful in 36s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 4m7s
CI / integration_tests (push) Successful in 4m41s
CI / docker (push) Successful in 44s
CI / coverage (push) Successful in 4m56s
CI / benchmark-publish (push) Successful in 17m17s
CI / benchmark-regression (pull_request) Successful in 31m25s
Implemented lazy container activation for devcontainer-instance resources with ContainerLifecycleState enum tracking six states (inactive, starting, active, stopping, stopped, error) with validated transitions. Extended DevcontainerHandler with devcontainer up CLI integration and JSON output parsing for container start. Added periodic health checking via devcontainer exec ping with configurable interval. Added agents resource stop and agents resource rebuild CLI commands for manual lifecycle control. Wired session close and plan completion hooks to automatic container cleanup. Includes lifecycle state persistence in resource registry with timestamped transitions. Added Behave BDD tests, Robot integration tests, and ASV activation latency benchmarks. - Added remoteWorkspaceFolder absolute-path validation - Aligned spec: handler name, rebuild types, --yes flag on stop/rebuild - Added registry re-read in stop_container success path for consistency - Added session_id field to ContainerLifecycleTracker for scoped cleanup - Scoped stop_all_active_containers to session_id when provided - Wired _cleanup_devcontainers into fail_apply and fail_execute - Wired start_health_check into activate_container success path - Restructured facade session close to always run container cleanup even without session service (F4) - Re-read tracker from registry in activate_container success path - Added evict_terminal_trackers to cap registry growth - Updated devcontainer_resources.md: health check auto-start, scoped cleanup hooks, known limitations for eviction and sandbox_strategy - Wired evict_terminal_trackers into stop_all_active_containers so terminal-state trackers are actually evicted in production - Made stop_container idempotent: returns early when container is already in a terminal state instead of raising ValueError - Fixed benchmark health check thread leak in TimeActivationLatency by clearing registry after each timing loop - Added rebuild pass-through (--reset-container flag to devcontainer up) - Added host_workspace_path field on ContainerLifecycleTracker so health probes use the host-side path for devcontainer exec - Wired lazy activation into DevcontainerHandler.resolve() for devcontainer-instance resources in non-running states - Changed _default_strategy from SNAPSHOT to NONE (container itself provides isolation; SandboxFactory raises NotImplementedError for snapshot) - Restricted _STOPPABLE_TYPES to devcontainer-instance only (container-instance is not directly stoppable via CLI) ISSUES CLOSED: #514
218 lines
12 KiB
Gherkin
218 lines
12 KiB
Gherkin
Feature: Devcontainer Activation, Stop and Rebuild
|
|
As a CleverAgents developer
|
|
I want devcontainers to be lazily activated and manually stopped or rebuilt
|
|
So that containers start on demand and can be controlled explicitly
|
|
|
|
# ── Lazy activation ────────────────────────────────────────
|
|
|
|
Scenario: Lazy activation succeeds on inactive container
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000010" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the container ID should be "aabbccddee0011223344"
|
|
And the workspace path should be "/workspaces/project"
|
|
And the runner should have received a devcontainer up call
|
|
|
|
Scenario: Lazy activation transitions to error on failure
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed activation
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000011" at "/workspace/project"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000011" should be "failed"
|
|
|
|
# ── Manual stop ────────────────────────────────────────────
|
|
|
|
Scenario: Stop transitions active container to stopped
|
|
Given a mock devcontainer CLI runner
|
|
And an active container "01TESTLIFECYCLE0000000020" with container ID "ctr-123"
|
|
When I stop container "01TESTLIFECYCLE0000000020"
|
|
Then the container state for "01TESTLIFECYCLE0000000020" should be "stopped"
|
|
And the runner should have received a docker stop call
|
|
|
|
# ── Rebuild ────────────────────────────────────────────────
|
|
|
|
Scenario: Rebuild transitions stopped container to active
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000030"
|
|
When I rebuild container "01TESTLIFECYCLE0000000030" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000030" should be "running"
|
|
|
|
Scenario: Rebuild transitions error container to active
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And an errored container "01TESTLIFECYCLE0000000031"
|
|
When I rebuild container "01TESTLIFECYCLE0000000031" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000031" should be "running"
|
|
|
|
# ── Argument validation ────────────────────────────────────
|
|
|
|
Scenario: Activate rejects empty resource_id
|
|
When I attempt to activate a container with empty resource_id
|
|
Then the activation should raise ValueError with "resource_id"
|
|
|
|
Scenario: Activate rejects empty workspace_folder
|
|
When I attempt to activate a container with empty workspace_folder
|
|
Then the activation should raise ValueError with "workspace_folder"
|
|
|
|
Scenario: Stop rejects empty resource_id
|
|
When I attempt to stop a container with empty resource_id
|
|
Then the stop should raise ValueError with "resource_id"
|
|
|
|
# ── Activate on already-running container ────────────────────
|
|
|
|
Scenario: Activate on already-running container raises ValueError
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And an active container "01TESTLIFECYCLE0000000140" with container ID "ctr-dup"
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000140" at "/workspace"
|
|
Then the activation should raise ValueError with "Invalid lifecycle transition"
|
|
|
|
# ── Stop container with no container_id ─────────────────────
|
|
|
|
Scenario: Stop container with no container_id skips docker stop
|
|
Given a mock devcontainer CLI runner
|
|
And an active container with no container_id "01TESTLIFECYCLE0000000150"
|
|
When I stop container "01TESTLIFECYCLE0000000150"
|
|
Then the container state for "01TESTLIFECYCLE0000000150" should be "stopped"
|
|
And the runner should not have received a docker stop call
|
|
|
|
# ── Activation fails when container_id is missing ────────────
|
|
|
|
Scenario: Activation fails when devcontainer up returns no container_id
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for activation with no container_id
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000160" at "/workspace"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000160" should be "failed"
|
|
|
|
# ── Direct rebuild_container coverage ───────────────────────
|
|
|
|
Scenario: rebuild_container directly stops then reactivates
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000090"
|
|
When I call rebuild_container for "01TESTLIFECYCLE0000000090" at "/workspace"
|
|
Then the container state for "01TESTLIFECYCLE0000000090" should be "running"
|
|
|
|
Scenario: rebuild_container rejects empty resource_id
|
|
When I call rebuild_container with empty resource_id
|
|
Then the rebuild should raise ValueError with "resource_id"
|
|
|
|
Scenario: rebuild_container rejects empty workspace_folder
|
|
When I call rebuild_container with empty workspace_folder
|
|
Then the rebuild should raise ValueError with "workspace_folder"
|
|
|
|
# ── Stop container error path coverage ─────────────────────
|
|
|
|
Scenario: stop_container transitions to error when docker stop raises
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured to raise exception on stop
|
|
And an active container "01TESTLIFECYCLE0000000110" with container ID "ctr-sf"
|
|
When I attempt to stop container "01TESTLIFECYCLE0000000110"
|
|
Then the stop should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000110" should be "failed"
|
|
|
|
# ── Activate generic exception path coverage ───────────────
|
|
|
|
Scenario: activate_container transitions to error on generic exception
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured to raise generic exception on up
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000120" at "/workspace"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000120" should be "failed"
|
|
|
|
# ── Stop container non-zero return code (F2) ───────────────
|
|
|
|
Scenario: stop_container transitions to error when docker stop returns non-zero
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed docker stop
|
|
And an active container "01TESTLIFECYCLE0000000170" with container ID "ctr-rc1"
|
|
When I attempt to stop container "01TESTLIFECYCLE0000000170"
|
|
Then the stop should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000170" should be "failed"
|
|
|
|
# ── R20: workspace_folder path validation ──────────────────
|
|
|
|
Scenario: Activate rejects relative workspace_folder
|
|
When I attempt to activate a container with relative workspace_folder
|
|
Then the activation should raise ValueError with "absolute path"
|
|
|
|
# ── S2: Activation timeout transitions to failed ───────────
|
|
|
|
Scenario: Activation timeout transitions container to failed state
|
|
Given a mock devcontainer CLI runner that times out
|
|
And a lifecycle tracker for resource "01TESTLIFECYCLE0000000350"
|
|
When I attempt to activate the timed-out container
|
|
Then the activation should raise RuntimeError with "timed out"
|
|
And the tracker "01TESTLIFECYCLE0000000350" should be in "failed" state
|
|
|
|
# ── F4: Orphaned container cleanup on timeout ──────────────
|
|
|
|
Scenario: Activation timeout attempts to stop orphaned containers
|
|
Given a mock devcontainer CLI runner that times out with orphan containers
|
|
And a lifecycle tracker for resource "01TESTLIFECYCLE0000000360"
|
|
When I attempt to activate the timed-out container with orphan cleanup
|
|
Then the activation should raise RuntimeError with "timed out"
|
|
And the runner should have attempted orphan container cleanup
|
|
|
|
# ── F6: R10 fix explicit assertion (container_id clearing) ─
|
|
|
|
Scenario: Stopped container has container_id cleared
|
|
Given a mock devcontainer CLI runner
|
|
And an active container "01TESTLIFECYCLE0000000380" with container ID "ctr-r10"
|
|
When I stop container "01TESTLIFECYCLE0000000380"
|
|
Then the container state for "01TESTLIFECYCLE0000000380" should be "stopped"
|
|
And the container_id for "01TESTLIFECYCLE0000000380" should be None
|
|
And the workspace_path for "01TESTLIFECYCLE0000000380" should be None
|
|
|
|
Scenario: Failed activation clears container_id
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed activation
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000381" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000381" should be "failed"
|
|
And the container_id for "01TESTLIFECYCLE0000000381" should be None
|
|
|
|
# ── R8-F2: stop_container early return on terminal state ────
|
|
|
|
Scenario: stop_container returns early when container already stopped
|
|
Given a mock devcontainer CLI runner
|
|
And a stopped container "01TESTLIFECYCLE0000000660"
|
|
When I stop container "01TESTLIFECYCLE0000000660" expecting early return
|
|
Then the container state for "01TESTLIFECYCLE0000000660" should be "stopped"
|
|
And the runner should not have received a docker stop call
|
|
|
|
Scenario: stop_container returns early when container already failed
|
|
Given a mock devcontainer CLI runner
|
|
And an errored container "01TESTLIFECYCLE0000000670"
|
|
When I stop container "01TESTLIFECYCLE0000000670" expecting early return
|
|
Then the container state for "01TESTLIFECYCLE0000000670" should be "failed"
|
|
And the runner should not have received a docker stop call
|
|
|
|
# ── F15: host_workspace_path stored on activation ───────────
|
|
|
|
Scenario: Activation stores host_workspace_path for health probes
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000800" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the host_workspace_path for "01TESTLIFECYCLE0000000800" should be "/workspace/project"
|
|
|
|
# ── F10: rebuild passes --reset-container flag ──────────────
|
|
|
|
Scenario: Rebuild passes --reset-container to devcontainer up
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000810"
|
|
When I call rebuild_container for "01TESTLIFECYCLE0000000810" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000810" should be "running"
|
|
And the devcontainer up call should include "--reset-container"
|
|
|
|
Scenario: Normal activation does not pass --reset-container
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000811" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the devcontainer up call should not include "--reset-container"
|