Commit Graph

2 Commits

Author SHA1 Message Date
Anso d113004359 feat: add confirmed Take down stack action with optional volume removal (#1599)
* feat: add confirmed Take down stack action with optional volume removal

Expose Take down in the stack header and sidebar with a confirmation dialog
that runs compose down while keeping the stack definition on disk. Optional
volume removal is gated by node capability and stack:deploy permission, with
remote gateway preflight before proxying removeVolumes requests.

Closes #1582

* fix: reset take-down volume checkbox when dialog closes

* test: align getStackMenuVisibility assertions with showTakeDown key

getStackMenuVisibility now returns a fifth lifecycle flag, showTakeDown,
but three exhaustive toEqual assertions still listed only the prior four
keys and failed. Add the expected showTakeDown value to each: true for
the partial and exited running-stack cases, false for the self stack.

* test: cover Take down visibility for running non-self stacks

The getStackMenuVisibility assertions exercised the partial and exited
branches and the self-stack guard, but not the raw === 'running' literal
that drives showTakeDown for a normal running stack. Add a case so a
regression dropping 'running' from that check is caught.

* fix: drop Take down from header overflow and wire activity shortcut

Remove duplicate Take down from More actions.

Keep inline button when running, sidebar menu, and Cmd+ArrowDown.

Record stack_taken_down in activity on successful POST /down.
2026-07-09 12:20:13 -04:00
Anso 009ec43638 feat(stacks): structured 503 docker_unavailable envelope + disconnect tests (#1191)
Stack lifecycle routes used to surface raw ECONNREFUSED text to the
client whenever the Docker daemon was unreachable. The frontend had no
way to distinguish "daemon down" from any other 500 and would render
the raw error message.

Detect daemon-reachability failures inside the route layer and surface
a structured envelope so the UI can render a dedicated "Docker is down"
state and operators can branch on a stable code:

  HTTP 503 { error: <message>, code: 'docker_unavailable' }

Detection lives in isDockerUnavailableError (exported from
routes/stacks.ts). The match is intentionally permissive across error
shapes Dockerode and the docker compose CLI produce: NodeJS ECONNREFUSED
errors with .code, ENOENT on docker.sock, and the CLI's
"Cannot connect to the Docker daemon" string. The helper is unit-tested
in isolation as well as exercised end-to-end through the route.

Applied to the five lifecycle routes that can hit the daemon-down path:
  POST /api/stacks/:name/restart  (via bulkContainerOp)
  POST /api/stacks/:name/stop     (via bulkContainerOp)
  POST /api/stacks/:name/start    (via bulkContainerOp)
  POST /api/stacks/:name/deploy
  POST /api/stacks/:name/down
  POST /api/stacks/:name/update

Adds ContainerActionOutcome variant 'docker-unavailable' so the route
can branch on the typed outcome rather than string-matching error
messages a second time.

11 integration tests in stack-docker-disconnect.test.ts cover:
  - isDockerUnavailableError matches ECONNREFUSED, CLI text, ENOENT on
    docker.sock; rejects unrelated errors and null/undefined.
  - restart/stop/start return 503 + code on Dockerode listContainers
    refusing.
  - deploy/down/update return 503 + code when ComposeService rejects
    with daemon-down error.
  - Unrelated deploy failures (YAML parse error) still return 500
    without the code, confirming the discriminator is correctly scoped.

Resolves L-3 from the stack-management audit.
2026-05-24 15:43:36 -04:00