update docs

This commit is contained in:
2026-08-09 19:00:55 +02:00
parent 4bb3507aec
commit c4d54d153f
3 changed files with 78 additions and 64 deletions
+17 -19
View File
@@ -17,15 +17,17 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md).
## Phase 1 — Core logic + navigation (done, initial version) ## Phase 1 — Core logic + navigation (done, initial version)
- [x] `internal/kctl`: pure, unit-tested logic — - [x] `internal/kctl`: pure, unit-tested logic —
context-pair matching (`FindNextContext`), namespace/label filtering template resolution (`ResolveTemplate`), namespace/label filtering
(`DistinctLabelValues`, `NamespacesForLabelValue`), and secret diffing (`DistinctLabelValues`, `NamespacesForLabelValue`), and secret diffing
(`DiffSecretValues`, `AnyMismatch`). (`DiffSecretValues`, `AnyMismatch`).
- [x] `internal/config`: YAML config loading (`context_pairs`, - [x] `internal/config`: YAML config loading with template-based context
`team_label_key`), with safe defaults when no config file exists yet. and secret name resolution (`ContextTemplate`, `SecretNameTemplate`,
`K8sSecretNameTemplate`), with safe defaults when no config file
exists yet.
- [x] `internal/kubeexec`: thin wrappers around `kubectl`/`aws` CLI calls - [x] `internal/kubeexec`: thin wrappers around `kubectl`/`aws` CLI calls
(contexts, namespaces, deployments, rollout restart/status, listing (namespaces, deployments, rollout restart/status, fetching AWS
AWS secrets, reading all fields of a Kubernetes secret, ExternalSecret secrets by template-resolved ID, reading all fields of a Kubernetes
annotation). secret, ExternalSecret annotation, AWS auth check).
- [x] `cmd/kctl-tui` "full" mode: Bubble Tea navigation for - [x] `cmd/kctl-tui` "full" mode: Bubble Tea navigation for
context -> team -> namespace, with `Esc` correctly popping back one context -> team -> namespace, with `Esc` correctly popping back one
level at a time, defaults pre-selected from the currently active level at a time, defaults pre-selected from the currently active
@@ -38,13 +40,13 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md).
- [x] `cmd/kctl-tui` "panel" mode: - [x] `cmd/kctl-tui` "panel" mode:
- Redeploy: pick a deployment from a list, confirm, then - Redeploy: pick a deployment from a list, confirm, then
`rollout restart` + `rollout status`. `rollout restart` + `rollout status`.
- Secrets: pick an AWS region, then pick the actual secret from a - Secrets: AWS auth check with interactive SSO login fallback,
**list of all AWS Secrets Manager secrets** in that region (no more then automatically resolve the AWS secret ID (from
manual secret-ID typing), enter the matching Kubernetes secret `secret_name_template`) and Kubernetes secret name (from
name, and automatically diff **every field** of both secrets in one `k8s_secret_name_template`), fetch both, diff **every field**
table (key / AWS value / Kubernetes value / match status). If any in one table (key / AWS value / Kubernetes value / match status).
field differs, offer a single force-sync request for the **whole If any field differs, offer a single force-sync request for the
secret** (one ExternalSecret annotation), not per individual field. **whole secret** (one ExternalSecret annotation).
- `Esc` closes the whole tmux session (`tmux kill-session`). - `Esc` closes the whole tmux session (`tmux kill-session`).
## Phase 2 — Hardening (open) ## Phase 2 — Hardening (open)
@@ -68,9 +70,8 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md).
- [ ] Detect OS at runtime; on native Windows (no WSL), fall back to - [ ] Detect OS at runtime; on native Windows (no WSL), fall back to
`wt.exe split-pane` instead of `tmux` for the status panes. `wt.exe split-pane` instead of `tmux` for the status panes.
- [ ] Document/implement that `Tab`-based context switching and - [ ] Document/implement that `Esc`-triggered session close is **not**
`Esc`-triggered session close are **not** available in the native available in the native Windows fallback — the panes must be closed
Windows fallback, per SPEC.md 3.6 — the panes must be closed
manually there. manually there.
## Phase 4 — Nice-to-haves (open, not committed) ## Phase 4 — Nice-to-haves (open, not committed)
@@ -82,9 +83,6 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md).
kubeconfig. kubeconfig.
- [ ] Homebrew tap / `scoop` manifest as additional install options - [ ] Homebrew tap / `scoop` manifest as additional install options
alongside `install.sh`. alongside `install.sh`.
- [ ] Optional heuristic to suggest a matching Kubernetes secret name for
a chosen AWS secret (e.g. by common naming convention), instead of
always asking for it manually.
## Notes for contributors ## Notes for contributors
+5
View File
@@ -125,6 +125,7 @@ aws_region: "eu-central-1"
aws_account_id: "123456789012" aws_account_id: "123456789012"
secret_name_template: "tf-{namespace}-{env}-secrets" secret_name_template: "tf-{namespace}-{env}-secrets"
k8s_secret_name_template: "{namespace}-common-secrets"
context_template: "arn:aws:eks:{region}:{account_id}:cluster/tf-{env}-{context}-1" context_template: "arn:aws:eks:{region}:{account_id}:cluster/tf-{env}-{context}-1"
team_label_key: "example.org/team" team_label_key: "example.org/team"
@@ -144,6 +145,10 @@ aws_sso_login_command: "aws sso login"
- `secret_name_template`: builds the AWS Secrets Manager secret ID from - `secret_name_template`: builds the AWS Secrets Manager secret ID from
the chosen namespace and environment. Placeholders: `{namespace}`, the chosen namespace and environment. Placeholders: `{namespace}`,
`{env}`. `{env}`.
- `k8s_secret_name_template`: builds the Kubernetes secret name from the
chosen namespace. Kept separate from `secret_name_template` because the
two sides commonly follow different naming conventions. Placeholders:
`{namespace}`.
- `context_template`: builds the actual kubectl context name/ARN from - `context_template`: builds the actual kubectl context name/ARN from
region, account ID, environment, and context. Placeholders: `{region}`, region, account ID, environment, and context. Placeholders: `{region}`,
`{account_id}`, `{env}`, `{context}`. Adjust the literal parts (`tf-`, `{account_id}`, `{env}`, `{context}`. Adjust the literal parts (`tf-`,
+56 -45
View File
@@ -5,7 +5,7 @@
A single terminal tool as the central entry point for everyday Kubernetes A single terminal tool as the central entry point for everyday Kubernetes
work, bundling the most common workflows currently done via long work, bundling the most common workflows currently done via long
`kubectl`/`k9s`/`aws-cli` commands, operable through a text UI (arrow keys, `kubectl`/`k9s`/`aws-cli` commands, operable through a text UI (arrow keys,
Esc, Tab) instead of long typed commands. Esc) instead of long typed commands.
Target platform: **Linux / WSL** (primary usage scenario, since split Target platform: **Linux / WSL** (primary usage scenario, since split
panes require a real terminal multiplexer). Native Windows (without WSL) panes require a real terminal multiplexer). Native Windows (without WSL)
@@ -49,9 +49,6 @@ Navigation:
(`tmux kill-session`, closing both k9s panes as well) and then moves the (`tmux kill-session`, closing both k9s panes as well) and then moves the
Go tool's screen stack one level up: namespace selection -> team Go tool's screen stack one level up: namespace selection -> team
selection -> context selection. selection -> context selection.
- **Tab** in the control pane switches the context pair according to the
context-pair pattern (see 3.6) for both status panes simultaneously; the
namespace stays the same.
### 3.2 Namespace grouping via labels ### 3.2 Namespace grouping via labels
@@ -64,10 +61,10 @@ Navigation:
kubectl get ns -o jsonpath='{range .items[*]}{.metadata.labels["<team-label-key>"]}{"\n"}{end}' | sort -u kubectl get ns -o jsonpath='{range .items[*]}{.metadata.labels["<team-label-key>"]}{"\n"}{end}' | sort -u
``` ```
- The actual label key is project-specific and set via the configuration - The actual label key is project-specific and set via the configuration
file (see 3.6), not hardcoded. file (see `team_label_key` in config), not hardcoded.
- Namespace labeling is a prerequisite (one-time setup outside the tool). - Namespace labeling is a prerequisite (one-time setup outside the tool).
### 3.3 Layout: 3-panel view (core design change vs. earlier drafts) ### 3.3 Layout: 3-panel view
Once start navigation is complete, the tool opens a tmux session with Once start navigation is complete, the tool opens a tmux session with
**three panes**, started with a single command: **three panes**, started with a single command:
@@ -93,10 +90,12 @@ Example startup command (generic placeholders):
``` ```
tmux new-session -d -s kctl \ tmux new-session -d -s kctl \
"kctl-tui panel --ctx=$CTX_A --ns=$NS --team=$TEAM" \; \ "kctl-tui panel --context=$CTX_A --ns=$NS --team=$TEAM" \; \
split-window -v "k9s --context $CTX_A -n $NS" \; \ set-option -t kctl remain-on-exit on \; \
split-window -v "k9s --context $CTX_B -n $NS" \; \ split-window -v -t kctl:0.0 "k9s --context $CTX_A -n $NS" \; \
select-layout main-horizontal \; \ split-window -v -t kctl:0.1 "k9s --context $CTX_B -n $NS" \; \
select-layout -t kctl even-vertical \; \
select-pane -t kctl:0.0 \; \
attach -t kctl attach -t kctl
``` ```
@@ -115,54 +114,66 @@ Switching between panes: `Ctrl-b` + arrow key, or `Ctrl-b` `o`.
### 3.5 AWS Secrets Manager <-> Kubernetes Secret diff — in the control pane ### 3.5 AWS Secrets Manager <-> Kubernetes Secret diff — in the control pane
1. Load the secret from AWS Secrets Manager: 1. Before entering the secrets workflow, verify the AWS session is valid
(`aws sts get-caller-identity`). If expired, offer to run the
configured SSO login command interactively.
2. Resolve the AWS Secrets Manager secret ID from `secret_name_template`
(using namespace + env) and the Kubernetes secret name from
`k8s_secret_name_template` (using namespace). No manual input required
for either name.
3. Fetch the AWS secret:
`aws secretsmanager get-secret-value --secret-id <secret-id> --region <region> --query SecretString --output text`. `aws secretsmanager get-secret-value --secret-id <secret-id> --region <region> --query SecretString --output text`.
2. Show the contained keys for selection. 4. Fetch all fields of the Kubernetes secret and base64-decode them:
3. Load the matching Kubernetes secret field: `kubectl -n <ns> get secret <secret-name> -o json`.
`kubectl -n <ns> get secret <secret-name> -o jsonpath='{.data.<field>}'`, 5. Compare every field at once in a table (key / AWS value / Kubernetes
base64-decode it. value / match status).
4. Compare the values (identical / different). 6. On mismatch, optionally request a force-sync for the whole secret:
5. On mismatch, optionally request a force-sync:
`kubectl -n <ns> annotate externalsecret <name> force-sync=<unix-timestamp> --overwrite`. `kubectl -n <ns> annotate externalsecret <name> force-sync=<unix-timestamp> --overwrite`.
All names (secret ID, secret name, field name, ExternalSecret name) are The ExternalSecret object name for the force-sync annotation is the only
asked for interactively at runtime, never hardcoded in the tool. value asked for interactively at runtime.
### 3.6 Context-pair pattern (configurable) — drives both status panes at once ### 3.6 Context resolution via templates
Requirement: the Tab switch in the control pane must switch **both** The actual kubectl context name/ARN for each environment is computed from
status panes below it, not just an internal state. a configurable template at startup. The two k9s status panes and all
kubectl calls in the control pane use the resolved context.
Configuration format (e.g. `~/.kctl-tui/config.yaml`), purely illustrative Configuration format (e.g. `~/.kctl-tui/config.yaml`), purely illustrative
with generic placeholders: with generic placeholders:
```yaml ```yaml
context_pairs: contexts:
- name: "environment-pair-1" - "internal"
contexts: ["<context-a1>", "<context-a2>"] - "external"
- name: "environment-pair-2" default_context: "internal"
contexts: ["<context-b1>", "<context-b2>"]
envs:
- "beta"
- "prod"
aws_region: "eu-central-1"
aws_account_id: "123456789012"
context_template: "arn:aws:eks:{region}:{account_id}:cluster/tf-{env}-{context}-1"
secret_name_template: "tf-{namespace}-{env}-secrets"
k8s_secret_name_template: "{namespace}-common-secrets"
team_label_key: "<organization>/<label-name>" team_label_key: "<organization>/<label-name>"
``` ```
Behavior on Tab in the control pane: The `context_template` replaces `{region}`, `{account_id}`, `{env}`, and
`{context}` placeholders with the configured values and the currently
selected environment/context. The resolved value must match an existing
context in your kubeconfig (e.g. added via `aws eks update-kubeconfig`).
1. Determine the current context pair from the configuration. **Platform limitation on Windows without WSL:** `kill-session` is
2. Restart both k9s panes via tmux-specific. Windows Terminal (`wt.exe`) offers no equivalent scripting
`tmux respawn-pane -k -t kctl:0.1 "k9s --context <newA> -n <ns>"` and to end the session from inside a pane. On plain Windows (without WSL),
`... kctl:0.2 ...` (namespace stays the same). only a simplified flow is possible: k9s panes are closed manually (`q`,
3. If the current context is in no configured list: show a hint in the then `Ctrl+Shift+W`); automatic session termination is unavailable there.
control pane instead of an error. This limitation is the main reason the primary target system is set to
4. No action outside this configuration — no error, only a hint. Linux/WSL.
**Platform limitation on Windows without WSL:** `respawn-pane`/
`kill-session` are tmux-specific. Windows Terminal (`wt.exe`) offers no
equivalent scripting to replace panes or end the session from inside a
pane. On plain Windows (without WSL), only a simplified flow is possible:
k9s panes are closed manually (`q`, then `Ctrl+Shift+W`); Tab switching and
automatic session termination are unavailable there. This limitation is
the main reason the primary target system is set to Linux/WSL.
## 4. Non-functional requirements ## 4. Non-functional requirements
@@ -240,8 +251,8 @@ Rejected options (see discussion history):
- Split view v1 fixed at 2 status panes + 1 control pane (3 panes total). - Split view v1 fixed at 2 status panes + 1 control pane (3 panes total).
- No RBAC/permission checks before executing sensitive actions — the tool - No RBAC/permission checks before executing sensitive actions — the tool
assumes existing kubectl permissions. assumes existing kubectl permissions.
- Configuration file format (`config.yaml`) is a proposal, not finally - Configuration file format (`config.yaml`) is defined and implemented;
agreed; concrete label keys, context names, and namespace names are concrete label keys, context names, and namespace names are
project-specific and belong exclusively in the user's local, unversioned project-specific and belong exclusively in the user's local, unversioned
configuration, not in this document or the source code. configuration, not in this document or the source code.
- Native Windows (without WSL) remains a secondary platform with manual - Native Windows (without WSL) remains a secondary platform with manual