diff --git a/AGENTS.md b/AGENTS.md index f281fd33..6947f27e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -289,7 +289,7 @@ curl -X POST -H "Authorization: token $TOK" -H 'Content-Type: application/json' ``` ## Common Operations -- **`homelab` CLI** (`/usr/local/bin/homelab`, source `cli/`): unified infra-ops verbs — run `homelab manifest` to discover the surface (each verb tagged read/write). v0.1 covers the inner loop: `homelab tf plan|fmt|apply ` (wraps `scripts/tg`; `apply` auto-claims presence + releases on exit, warns out-of-band), `homelab claim|release :`, `homelab work start|land|clean ` (worktree lifecycle; `land` gates on verification, `--verify-cmd`/`--no-verify`). Full docs: `cli/README.md`. +- **`homelab` CLI** (`/usr/local/bin/homelab`, source `cli/`): unified infra-ops verbs — run `homelab manifest` to discover the surface (each verb tagged read/write). Infra loop: `homelab tf plan|fmt|apply ` (wraps `scripts/tg`; `apply` auto-claims presence + releases on exit, warns out-of-band), `homelab claim|release :`, `homelab work start|land|clean ` (worktree lifecycle; `land` gates on verification, `--verify-cmd`/`--no-verify`). Kubernetes (v0.2): `homelab k8s status|get|logs|describe|debug|pf|rollout-status ` (read; `` defaults to the namespace, target to `deploy/`), `homelab k8s db [--mysql] -- ""`, `k8s exec`, `k8s restart`, `k8s rm-pod` (pods/jobs only) — config-mutation kubectl verbs are intentionally absent (Terraform-only). Full docs: `cli/README.md`. - **Deploy new service**: Use `stacks//` as template. Create stack, add DNS in tfvars, apply platform then service. - **Fix crashed pods**: Run healthcheck first. Safe to delete evicted/failed pods and CrashLoopBackOff pods with >10 restarts. - **OOMKilled**: Check `kubectl describe limitrange tier-defaults -n `. Increase `resources.limits.memory` in the stack's main.tf. diff --git a/cli/README.md b/cli/README.md index 80ea0f52..8aeb2e85 100644 --- a/cli/README.md +++ b/cli/README.md @@ -32,6 +32,30 @@ homelab version | `work land [--verify-cmd "…"] [--no-verify]` | write | merge master in → verify → push `HEAD:master` (non-ff retry; PR fallback) | | `work clean ` | write | remove a task's worktree + branch (run from the main checkout) | +### v0.2 verbs — Kubernetes + +Built on an **app→namespace→pod resolver**: `` defaults to the namespace +(most namespaces hold one app); the target defaults to `deploy/` and lets +kubectl resolve the pod. Override with `-n`/`--pod`/`-c`/`-l`/`--tty`. Uses the +ambient kubeconfig. + +| Command | Tier | What it does | +|---|---|---| +| `k8s status [ns]` | read | pods (wide) + recent non-Normal events (`-A` if no ns) | +| `k8s get […]` | read | `kubectl -n get …` passthrough | +| `k8s logs ` | read | logs for `deploy/` (`--tail` default 200; `-c`/`--previous`/`--since`/`-l`) | +| `k8s describe [resource]` | read | describe the deployment (or an explicit resource) | +| `k8s debug ` | read | one-shot triage: pods + workloads + describe + recent logs + events | +| `k8s pf [target]` | read | port-forward to `svc/` (or an explicit target) | +| `k8s rollout-status ` | read | `rollout status deploy/` | +| `k8s db [--mysql] [--db N] -- ""` | write | exec into the dbaas DB (PG `pg-cluster-rw`, or MySQL with env-password wrapper) | +| `k8s exec [--tty] -- ` | write | exec in the app's pod | +| `k8s restart ` | write | `rollout restart deploy/` then wait for status | +| `k8s rm-pod -n [--job] [--force]` | write | delete a stuck **pod/job only** | + +Config-mutation verbs (`apply`/`edit`/`patch`/`scale`/`create`) are intentionally +**not** exposed — they stay raw `kubectl`, per the Terraform-only policy. + `tf` resolves the stack dir by walking up from cwd to the infra root and delegates to `scripts/tg` (which owns state decrypt/encrypt, the Vault lock, and the ingress auth-comment check). git-crypt filter flags are auto-injected on git @@ -65,4 +89,4 @@ original flag-based path unchanged, so the webhook handler is unaffected. ## Design -See `infra/docs/adr/0004`–`0006` for the architecture decisions. +See `infra/docs/adr/0004`–`0007` for the architecture decisions. diff --git a/cli/VERSION b/cli/VERSION index b82608c0..1474d00f 100644 --- a/cli/VERSION +++ b/cli/VERSION @@ -1 +1 @@ -v0.1.0 +v0.2.0 diff --git a/docs/adr/0007-homelab-k8s-verbs.md b/docs/adr/0007-homelab-k8s-verbs.md new file mode 100644 index 00000000..422b3431 --- /dev/null +++ b/docs/adr/0007-homelab-k8s-verbs.md @@ -0,0 +1,30 @@ +# homelab k8s verb-group: app→pod resolver, read/write split, config-mutation stays raw + +v0.2 adds the Kubernetes verb-group — the biggest remaining surface by far +(mining the post-v0.1 corpus: 11,291 `kubectl` commands across 243 sessions, more +than every other domain combined). + +It is built on an **app→namespace→pod resolver**: most namespaces hold exactly +one app, so `` defaults to the namespace, and the target defaults to +`deploy/` (kubectl resolves a pod from the Deployment). `-n`/`--pod`/`-c`/ +`-l`/`--tty` override; multi-pod namespaces (`dbaas`, `monitoring`) need +specificity. The CLI uses the ambient kubeconfig — no per-call auth flags. + +Verbs: read — `status`, `get`, `logs`, `describe`, `debug` (one-shot triage), +`pf`, `rollout-status`; write/operational — `db`, `exec`, `restart`, `rm-pod`. + +## Decisions worth recording + +- **Config-mutation verbs are deliberately NOT exposed** (`apply`/`edit`/`patch`/ + `scale`/`create`). They stay raw `kubectl`, by design, per the repo's + Terraform-only policy — the corpus confirms they're low-frequency, and a + friendly verb would normalise a policy violation. +- **`rm-pod` is restricted to pods/jobs only** — deleting Deployments/STS/PVCs is + config mutation and forbidden; the verb cannot target them. +- **`db` encodes the dbaas exec pattern** (the single highest-value k8s + sub-pattern, ~886 dbaas ops): PG via `pg-cluster-rw -c postgres`, + `psql -U postgres -d `; MySQL via `mysql-standalone-0` with a + `bash -c 'mysql -p"$MYSQL_ROOT_PASSWORD" …'` wrapper so the password comes from + the pod env and never appears on the command line. +- Read verbs were smoke-tested against the live cluster; write verbs are + unit-tested (resolver, db-plan, shell-quoting) but not fired at live state.