From c4d54d153f7359821685fd2ea674ea17113e3b7f Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Sun, 9 Aug 2026 19:00:55 +0200 Subject: [PATCH] update docs --- PLAN.md | 36 +++++++++---------- README.md | 5 +++ SPEC.md | 101 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 78 insertions(+), 64 deletions(-) diff --git a/PLAN.md b/PLAN.md index b182536..a769502 100644 --- a/PLAN.md +++ b/PLAN.md @@ -17,15 +17,17 @@ is still open. For the full requirements, see [SPEC.md](SPEC.md). ## Phase 1 — Core logic + navigation (done, initial version) - [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 (`DiffSecretValues`, `AnyMismatch`). -- [x] `internal/config`: YAML config loading (`context_pairs`, - `team_label_key`), with safe defaults when no config file exists yet. +- [x] `internal/config`: YAML config loading with template-based context + 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 - (contexts, namespaces, deployments, rollout restart/status, listing - AWS secrets, reading all fields of a Kubernetes secret, ExternalSecret - annotation). + (namespaces, deployments, rollout restart/status, fetching AWS + secrets by template-resolved ID, reading all fields of a Kubernetes + secret, ExternalSecret annotation, AWS auth check). - [x] `cmd/kctl-tui` "full" mode: Bubble Tea navigation for context -> team -> namespace, with `Esc` correctly popping back one 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: - Redeploy: pick a deployment from a list, confirm, then `rollout restart` + `rollout status`. - - Secrets: pick an AWS region, then pick the actual secret from a - **list of all AWS Secrets Manager secrets** in that region (no more - manual secret-ID typing), enter the matching Kubernetes secret - name, and automatically diff **every field** of both secrets in one - table (key / AWS value / Kubernetes value / match status). If any - field differs, offer a single force-sync request for the **whole - secret** (one ExternalSecret annotation), not per individual field. + - Secrets: AWS auth check with interactive SSO login fallback, + then automatically resolve the AWS secret ID (from + `secret_name_template`) and Kubernetes secret name (from + `k8s_secret_name_template`), fetch both, diff **every field** + in one table (key / AWS value / Kubernetes value / match status). + If any field differs, offer a single force-sync request for the + **whole secret** (one ExternalSecret annotation). - `Esc` closes the whole tmux session (`tmux kill-session`). ## 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 `wt.exe split-pane` instead of `tmux` for the status panes. -- [ ] Document/implement that `Tab`-based context switching and - `Esc`-triggered session close are **not** available in the native - Windows fallback, per SPEC.md 3.6 — the panes must be closed +- [ ] Document/implement that `Esc`-triggered session close is **not** + available in the native Windows fallback — the panes must be closed manually there. ## 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. - [ ] Homebrew tap / `scoop` manifest as additional install options 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 diff --git a/README.md b/README.md index 976d5de..1feabcf 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ aws_region: "eu-central-1" aws_account_id: "123456789012" 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" 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 the chosen namespace and environment. Placeholders: `{namespace}`, `{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 region, account ID, environment, and context. Placeholders: `{region}`, `{account_id}`, `{env}`, `{context}`. Adjust the literal parts (`tf-`, diff --git a/SPEC.md b/SPEC.md index e2f75db..e52282e 100644 --- a/SPEC.md +++ b/SPEC.md @@ -5,7 +5,7 @@ A single terminal tool as the central entry point for everyday Kubernetes work, bundling the most common workflows currently done via long `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 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 Go tool's screen stack one level up: namespace selection -> team 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 @@ -64,10 +61,10 @@ Navigation: kubectl get ns -o jsonpath='{range .items[*]}{.metadata.labels[""]}{"\n"}{end}' | sort -u ``` - 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). -### 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 **three panes**, started with a single command: @@ -93,10 +90,12 @@ Example startup command (generic placeholders): ``` tmux new-session -d -s kctl \ - "kctl-tui panel --ctx=$CTX_A --ns=$NS --team=$TEAM" \; \ - split-window -v "k9s --context $CTX_A -n $NS" \; \ - split-window -v "k9s --context $CTX_B -n $NS" \; \ - select-layout main-horizontal \; \ + "kctl-tui panel --context=$CTX_A --ns=$NS --team=$TEAM" \; \ + set-option -t kctl remain-on-exit on \; \ + split-window -v -t kctl:0.0 "k9s --context $CTX_A -n $NS" \; \ + 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 ``` @@ -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 -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 --region --query SecretString --output text`. -2. Show the contained keys for selection. -3. Load the matching Kubernetes secret field: - `kubectl -n get secret -o jsonpath='{.data.}'`, - base64-decode it. -4. Compare the values (identical / different). -5. On mismatch, optionally request a force-sync: +4. Fetch all fields of the Kubernetes secret and base64-decode them: + `kubectl -n get secret -o json`. +5. Compare every field at once in a table (key / AWS value / Kubernetes + value / match status). +6. On mismatch, optionally request a force-sync for the whole secret: `kubectl -n annotate externalsecret force-sync= --overwrite`. -All names (secret ID, secret name, field name, ExternalSecret name) are -asked for interactively at runtime, never hardcoded in the tool. +The ExternalSecret object name for the force-sync annotation is the only +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** -status panes below it, not just an internal state. +The actual kubectl context name/ARN for each environment is computed from +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 with generic placeholders: ```yaml -context_pairs: - - name: "environment-pair-1" - contexts: ["", ""] - - name: "environment-pair-2" - contexts: ["", ""] +contexts: + - "internal" + - "external" +default_context: "internal" + +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: "/" ``` -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. -2. Restart both k9s panes via - `tmux respawn-pane -k -t kctl:0.1 "k9s --context -n "` and - `... kctl:0.2 ...` (namespace stays the same). -3. If the current context is in no configured list: show a hint in the - control pane instead of an error. -4. No action outside this configuration — no error, only a hint. - -**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. +**Platform limitation on Windows without WSL:** `kill-session` is +tmux-specific. Windows Terminal (`wt.exe`) offers no equivalent scripting +to 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`); automatic session termination is unavailable there. +This limitation is the main reason the primary target system is set to +Linux/WSL. ## 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). - No RBAC/permission checks before executing sensitive actions — the tool assumes existing kubectl permissions. -- Configuration file format (`config.yaml`) is a proposal, not finally - agreed; concrete label keys, context names, and namespace names are +- Configuration file format (`config.yaml`) is defined and implemented; + concrete label keys, context names, and namespace names are project-specific and belong exclusively in the user's local, unversioned configuration, not in this document or the source code. - Native Windows (without WSL) remains a secondary platform with manual