prole/docs/completed/06-patch-garage-script-fixes.md
chrisfu 73dce037f3 docs: reorganise briefs into docs/completed/; add conf/service/knoe.cfg
docs/completed/ — new directory; 9 shipped Junie briefs moved from
docs/plans/junie/ (02-k3s-prole-rename, 03-image-rename, 06-patch-garage,
07-init-cnpg-gke-sa-wiring, 13-podmonitor, 15-remove-dead-dashboard,
k3d-knoe-auth-dev-loop, phase2-oidc-gke-deploy, todo-1-cfg-save-path-bug).
docs/completed/README.md summarises all shipped work with dates/refs.

docs/plans/junie/README.md — updated to 2026-05-23; active/pending tables
reflect current state; shipped section now points to docs/completed/.

conf/service/knoe.cfg — new unattended deploy config for the service/k3s
environment (used by ./deploy.sh).

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:32:01 -07:00

192 lines
7.8 KiB
Markdown

# Junie brief — Queue #6: fix `scripts/patch_garage_cross_cluster.sh`
> **Self-contained brief.** Three concrete defects in the script + a related
> manifest split. Land as one or two commits (your call — see §5).
---
## 1. Why
`scripts/patch_garage_cross_cluster.sh` was written one-shot during the
2026-04-29 cluster recovery to migrate CNPG backups off Garage and onto
GCS+Workload-Identity. It worked at the time, but reading it back surfaced
three defects that would bite the next person who runs it. We want it to
be re-runnable cleanly (e.g. for a future second customer or DR rebuild)
without surprises.
Live-cluster context (do not change): GCS migration is complete, ObjectStore
is active, ScheduledBackup is plugin-method, three CNPG pods on
`knoe-dev-cnpg-0` are healthy. **You don't need to run the script to fix
it.** This is a code-only commit.
## 2. The three defects
### Defect A — stale `DB_CLUSTER` default
`scripts/patch_garage_cross_cluster.sh:39`
```bash
DB_CLUSTER="${DB_CLUSTER:-knoe-cnpg-0}"
```
Should be `knoe-dev-cnpg-0` (matches `conf/gke.cfg` line `init_cluster.db_cluster_kubecontext`
and `CLAUDE.md` cluster table). The current default resolves to the wrong
context and `kubectl` operations would silently target the wrong cluster
(or fail with "context not found", depending on the environment).
The script's `APP_CLUSTER="${APP_CLUSTER:-knoe-dev-0}"` on line 40 is
already correct.
Header comment on lines 16-18 also references the wrong name in the
`./etc/init_cnpg_gke.sh ... --cluster knoe-cnpg-0` example — fix that too.
### Defect B — Phase 1 misses `service/garage-s3-ilb`
`scripts/patch_garage_cross_cluster.sh:86-95` — Phase 1's deletion loop:
```bash
for resource in "statefulset/garage" "service/garage" "configmap/garage-config"; do
...
done
```
The loop deletes `service/garage` (the ClusterIP) but not
`service/garage-s3-ilb` (the LoadBalancer). The ObjectStore's
`endpointURL` historically pointed at the ILB, so leaving the LB orphaned
costs money and confuses cluster state. The 2026-04-29 cleanup did this
manually after the script ran.
**Fix**: add `"service/garage-s3-ilb"` to the loop's resource list. Order
doesn't matter (all `--ignore-not-found=true`).
### Defect C — Phase 2 applies a manifest containing both SA AND a legacy ScheduledBackup
`scripts/patch_garage_cross_cluster.sh:114-122` — Phase 2 applies
`deploy/gcp/gke/knoe-db-backup-gcs.yaml`. That manifest currently contains:
1. **ServiceAccount `cnpg-backup-sa`** — wanted; this is the WI-bound SA.
2. **ScheduledBackup `knoe-db-daily`** with `method: barmanObjectStore`
**unwanted**. Two problems:
- Live cluster already has a plugin-method ScheduledBackup. Applying
this would create a duplicate that runs alongside the active one.
- `method: barmanObjectStore` (the legacy in-tree method) is being
removed in CNPG v1.30 per upstream release notes. New work should not
pin to it.
**Fix** (one of two paths — pick the cleanest):
**Path 1 (recommended): split the manifest.** Move the ScheduledBackup
out of `deploy/gcp/gke/knoe-db-backup-gcs.yaml` into its own file (or
delete it outright if there's no consumer — verify by `grep -r "knoe-db-daily"`
across the repo first; if nothing references it, just remove it). Keep
`knoe-db-backup-gcs.yaml` as the canonical SA-only manifest. The script's
Phase 2 stays unchanged (still applies the SA file).
**Path 2 (minimal): adjust the script only.** Use a kubectl filter so
Phase 2 only applies the ServiceAccount portion of the file:
```bash
GCP_PROJECT_ID="${GCP_PROJECT}" envsubst '${GCP_PROJECT_ID}' < "${GCS_MANIFEST}" \
| yq 'select(.kind == "ServiceAccount")' \
| kubectl --context="${DB_CTX}" apply -f -
```
(`yq` is already used elsewhere in `etc/`. If it's not in the script's
preflight `command -v` check, add it.)
**Path 1 is recommended** because it makes the manifest file's contents
match its filename's intent (`-gcs.yaml` should be GCS plumbing, not
backup scheduling), and removes the deprecated-method footgun for any
future runner.
If you take Path 1, also verify the live ScheduledBackup in
`knoe-db-0` namespace still exists and uses the plugin method — but **do
not run kubectl against the live cluster from the script change.** Just
inspect via `kubectl --context=$DB get scheduledbackup -n knoe-db-0 -o yaml`
once if you want to confirm; don't modify it.
### Defect D (bonus, mentioned in TODO) — Phase 3 unconditional re-apply
Phase 3 (lines 127-134) unconditionally re-applies
`k8s/knoe/knoe-db-barman-objectstore-gcs.yaml`. Manifest currently matches
live state, so this is harmless. The TODO suggests "worth a guard" — add
a `kubectl get objectstore knoe-db-barman-objectstore -n $DB_NS` check
that diffs against the manifest and skips re-apply if matching. **Skip
this one if it adds significant scope** — it's a nice-to-have, not a
defect.
## 3. What to verify
```bash
# 1) Defaults resolve correctly when no env vars set
bash -n scripts/patch_garage_cross_cluster.sh # syntax check
( unset DB_CLUSTER APP_CLUSTER GCP_PROJECT GCP_REGION SERVICE_NS DB_NS
source <(sed -n '36,50p' scripts/patch_garage_cross_cluster.sh)
echo "DB_CLUSTER=$DB_CLUSTER, DB_CTX=$DB_CTX" )
# Expected: DB_CLUSTER=knoe-dev-cnpg-0, DB_CTX=gke_..._knoe-dev-cnpg-0
# 2) Dry-run the script in CONFIRM=false mode (existing safety: prints the
# plan and exits 1) — verify the printed plan reads sensibly
./scripts/patch_garage_cross_cluster.sh
# Expected: prints "1. Remove Garage from knoe-dev-cnpg-0/knoe-system" etc.
# 3) Phase 1 deletion list now includes garage-s3-ilb
grep -A8 'for resource in' scripts/patch_garage_cross_cluster.sh
# Expected: list contains "service/garage-s3-ilb"
# 4) (If Path 1 chosen) the SA-only manifest has only ServiceAccount kind
grep '^kind:' deploy/gcp/gke/knoe-db-backup-gcs.yaml
# Expected: only "kind: ServiceAccount"
```
## 4. Out of scope
- Don't run the live script. This is a code-only fix. The script is for
someone migrating a fresh cluster; we're not migrating.
- Don't change any RBAC or IAM. The Workload Identity binding is correct
in the live cluster; this brief touches the script and (optionally) the
manifest split, nothing more.
- The CNPG SA wiring inside `etc/init_cnpg_gke.sh` (`cluster.spec.serviceAccountName`
+ extended RoleBindings) is queue item **#7** with its own brief. Don't
conflate.
## 5. Commit shape
If you take Path 1 (manifest split): two commits, atomic each:
1. `chore(deploy): split knoe-db-backup-gcs manifest — SA only, drop legacy ScheduledBackup`
2. `fix(scripts): patch_garage_cross_cluster defaults + missing garage-s3-ilb`
If you take Path 2 (script-only): one commit.
```
fix(scripts): patch_garage_cross_cluster — three defects from 2026-04-29 review
Defects A, B, C from docs/plans/junie/06-patch-garage-script-fixes.md:
A. DB_CLUSTER default was knoe-cnpg-0 (stale rebrand artifact);
should be knoe-dev-cnpg-0 to match conf/gke.cfg.
B. Phase 1 deletion loop missed service/garage-s3-ilb (the
LoadBalancer the ObjectStore endpoint historically pointed at).
Now included.
C. Phase 2 was applying knoe-db-backup-gcs.yaml whole, including a
legacy ScheduledBackup using method:barmanObjectStore (being
removed in CNPG v1.30). Manifest split: SA stays in
knoe-db-backup-gcs.yaml; ScheduledBackup removed.
Header comment + usage example also updated for the rename.
Closes queue item #6 in docs/TODO.md (drift R9).
```
## 6. Definition of done
- [ ] `DB_CLUSTER` default + header comment updated.
- [ ] Phase 1 loop includes `service/garage-s3-ilb`.
- [ ] Phase 2 doesn't apply a legacy ScheduledBackup (Path 1 or 2).
- [ ] `bash -n` clean; CONFIRM=false dry-run prints sensible plan.
- [ ] If Path 1: `kind: ServiceAccount` is the only kind in
`deploy/gcp/gke/knoe-db-backup-gcs.yaml`.
- [ ] `docs/TODO.md` queue item #6 + drift R9 row archived to **Done** with
date and commit ref.