Skip to content

feat(stack): use the exec hints of the server in stack pods, stack exec and the stream commands - #184

Merged
omattsson merged 1 commit into
mainfrom
feat/183-exec-hints
Oct 31, 2026
Merged

omattsson merged 1 commit into
mainfrom
feat/183-exec-hints

Conversation

@omattsson

Copy link
Copy Markdown
Owner

Closes #183.

Uses what k8s-stack-manager v0.15.0 (omattsson/k8s-stack-manager#728) sends:

  • the exec hints, ?exec_hints=true on /status and /pods: exec_hints, plus exec_containers and exec_default_container per chart
  • stream_api_keys_allowed in /ui-config

Changes

stack pods

  • Sends ?exec_hints=true; with -q it sends no query.
  • When the response has exec_hints: true, the table gets an EXEC column after REASON, so the other columns keep their positions:
    • web*, worker: * marks the container used without -c.
    • -: no container accepts exec.
  • Without hints (older server, or a caller who may not modify the stack), the table is unchanged; a golden file made from the old code checks this.
  • -o json and -o yaml print the fields as the server sends them.
  • Only valid Kubernetes container names are shown, so a name like web*, admin or one with $(...) can't fake the list or reach a copied shell command.
  • The CHART, POD and PHASE cells and the namespace are now sanitized too. That gap was there before this change.

stack exec refusals

  • Only for container_not_allowed, container_not_found and service_account_token, stackctl makes one best-effort /pods?exec_hints=true call. The hint then replaces the generic advice:
    • Containers listed: ... Use -c web or -c worker. The -c value that was refused is left out, and the list stops at 8 names.
    • Another chart has a list but this one doesn't: ... The server lists no container of chart X that accepts exec; ask an administrator.
    • No chart has a list: the server could not check, so the error stays as it is today.
  • A failed call leaves the error unchanged. The exit code stays 255, and other refusal codes make no extra call.

API key check before the dial (stack exec, stack port-forward)

  • With an API key from the flag, env or context, stackctl first reads the public /ui-config, without credentials.
  • stream_api_keys_allowed: false: the command stops before it looks up the stack or dials, with the shared advice to log in. Exec exits 255, port-forward exits 1.
  • Field missing, true, or the read fails: dial as before.
  • A login session makes no extra call.

Ctrl-C during these extra calls:

  • exec: "interrupted before the command started", exit 130
  • port-forward: exit 0, binding no port

Also

  • U+061C (Arabic letter mark) is treated as a bidi control when sanitizing.
  • The vendored swagger is refreshed.
  • Docs: the help texts, the README, cli/cmd/agent_guide.md and CLAUDE.md.

Tests

  • make lint: clean. go test -race ./...: all 8 packages ok (3194 passed, 2 skipped helper processes). go vet -tags live ./test/live/: ok.
  • Repeated runs: the exec, pods, port-forward and API-key tests with -count=3.
  • New tests:
    • stack pods: every output mode, with hints (one container, two, an empty list) and without, and the exec_hints=true query.
    • stack exec: each refusal code, with a working and with a failing hints call; exit code 255; no extra call for other outcomes.
    • API key check: 4 key sources × 7 /ui-config answers; no credentials sent; no call with a session; Ctrl-C.
    • Contract test for the new swagger fields.
  • A code review and a security review found no critical or high issues. Their findings are fixed; the ones left open predate this change and are listed below.

Left open (they predate this change)

  • No size cap on JSON response bodies.
  • A redirect to another host resends X-API-Key.
  • stack pods -q prints raw pod names.

🤖 Generated with Claude Code

…ec and the stream commands (#183)

k8s-stack-manager v0.15.0 sends exec hints (?exec_hints=true on /status
and /pods: exec_hints, exec_containers, exec_default_container) and
stream_api_keys_allowed in /ui-config.

- stack pods: requests the hints (not with -q) and shows an EXEC column
  after REASON (`web*, worker`, `*` = the container without -c, `-` =
  no container) when the server sends them; the table is unchanged
  without hints. -o json/yaml print the fields. Only valid container
  names are shown; the CHART, POD, PHASE cells and the namespace are
  sanitized.
- stack exec: a refusal with container_not_allowed, container_not_found
  or service_account_token makes one best-effort /pods?exec_hints=true
  call and names the accepted containers ("Use -c web or -c worker.")
  or says that the server lists none for the chart. A failed call leaves
  the error unchanged; the exit code stays 255.
- stack exec and stack port-forward with an API key read the public
  /ui-config first (no credentials) and stop before the dial when the
  server does not allow API keys for streams. A missing field or a failed
  read dials as before; a session makes no extra call.
- Ctrl-C during the extra calls: "interrupted before the command
  started" (exec 130); port-forward binds no port.
- U+061C is a bidi control for sanitizing. Vendored swagger refreshed.
- Docs: help texts, README, agent guide, CLAUDE.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Oct 31, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Share

@omattsson
omattsson merged commit b508dc8 into main Oct 31, 2026
8 checks passed
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.

Use the exec hints of the server in stack pods, stack exec and the stream commands

1 participant