database: mirror admin-app-db-url into westside-admin namespace #310

Merged
forgejo_admin merged 1 commit from 135-cross-ns-secret-mirror into main 2026-04-28 04:35:27 +00:00
Contributor

Summary

Adds a second kubernetes_secret_v1 in the westside-admin namespace that mirrors the admin-app-db-url secret created in basketball-api by PR #304. Both secrets derive DATABASE_URL from a single local.admin_app_database_url expression backed by var.admin_app_db_password, so a Salt-pillar password rotation propagates atomically to both namespaces in one make tofu-apply.

Decision per scope review on forgejo_admin/pal-e-deployments#135 (Lucas-approved 2026-04-25): option 4 (terraform mirror beside the source). Rejected cluster-wide replicators (kubernetes-replicator, ESO) and SOPS duplication.

Changes

  • terraform/modules/database/main.tf
    • Extracted DATABASE_URL value into local.admin_app_database_url so the source secret and the mirror reference the same expression
    • Added data "kubernetes_namespace_v1" "westside_admin" so apply fails fast if the deployments overlay has not yet created the namespace
    • Added kubernetes_secret_v1.admin_app_db_url_westside_admin -- same secret name (admin-app-db-url), same key (DATABASE_URL), in the westside-admin namespace, labeled mirror-of: basketball-api/admin-app-db-url
  • terraform/modules/database/outputs.tf
    • Added admin_app_db_url_secret_name and admin_app_db_url_namespaces outputs for debugging and operator visibility

tofu plan Output

tofu plan was not run locally because the operator-side k3s.tfvars (with the Salt-pillar-derived admin_app_db_password) is not in this worktree. CI will plan against the live state. tofu validate passes clean.

$ tofu fmt -recursive
(no changes)

$ tofu validate
Success! The configuration is valid.

Apply order

  1. pal-e-deployments overlay (already creates westside-admin namespace -- merged in #134)
  2. pal-e-platform make tofu-apply (this PR -- creates the mirror)
  3. The follow-up pal-e-deployments PR (rewires the deployment's DATABASE_URL to the new mirror) can merge in any order; ArgoCD will reconcile once the mirror exists.

Test Plan

  • CI runs the standard plan-step cleanly (this is the live integration test for #309's plan-step shell-escaping fix)
  • After make tofu-apply: kubectl get secret admin-app-db-url -n westside-admin -o jsonpath='{.data.DATABASE_URL}' | base64 -d matches the same query against -n basketball-api
  • Rotation test: bump admin_app_db_password in Salt pillar, make tofu-apply, confirm both secrets show new value, then kubectl rollout restart deployment/westside-admin -n westside-admin

Review Checklist

  • Terraform fmt clean, validate clean
  • No new variables, providers, or modules introduced
  • Source secret (admin_app_db_url on basketball-api) unchanged in shape -- only the value expression was extracted into a local
  • Mirror secret name matches source name (admin-app-db-url) so no consumer-side renaming is required
  • Namespace data source guards apply ordering with a clear failure mode
  • No cluster-wide controllers (kubernetes-replicator, ESO) introduced
  • CI plan step runs cleanly (validates #309 fix in production)
  • Decision review: review-135-2026-04-25 (Lucas-approved option 4)
  • Memory: feedback_tofu_lock_false, feedback_discovered_scope_always_tracked
  • Convention: cross-namespace secret distribution via terraform mirror (no replicator)
  • Closes forgejo_admin/pal-e-deployments#135
  • Builds on: pal-e-platform PR #304 (admin_app role + source secret), PR #308 (Salt pillar entry)
  • Follow-up: forgejo_admin/pal-e-deployments branch 135-drop-database-url-sops (drops SOPS placeholder + rewires secretKeyRef)
  • story:admin-row-crud
  • arch:postgres, arch:k8s-deploy

🤖 Generated with Claude Code

## Summary Adds a second `kubernetes_secret_v1` in the `westside-admin` namespace that mirrors the `admin-app-db-url` secret created in `basketball-api` by PR #304. Both secrets derive `DATABASE_URL` from a single `local.admin_app_database_url` expression backed by `var.admin_app_db_password`, so a Salt-pillar password rotation propagates atomically to both namespaces in one `make tofu-apply`. Decision per scope review on `forgejo_admin/pal-e-deployments#135` (Lucas-approved 2026-04-25): option 4 (terraform mirror beside the source). Rejected cluster-wide replicators (kubernetes-replicator, ESO) and SOPS duplication. ## Changes - `terraform/modules/database/main.tf` - Extracted `DATABASE_URL` value into `local.admin_app_database_url` so the source secret and the mirror reference the same expression - Added `data "kubernetes_namespace_v1" "westside_admin"` so apply fails fast if the deployments overlay has not yet created the namespace - Added `kubernetes_secret_v1.admin_app_db_url_westside_admin` -- same secret name (`admin-app-db-url`), same key (`DATABASE_URL`), in the `westside-admin` namespace, labeled `mirror-of: basketball-api/admin-app-db-url` - `terraform/modules/database/outputs.tf` - Added `admin_app_db_url_secret_name` and `admin_app_db_url_namespaces` outputs for debugging and operator visibility ## tofu plan Output `tofu plan` was not run locally because the operator-side `k3s.tfvars` (with the Salt-pillar-derived `admin_app_db_password`) is not in this worktree. CI will plan against the live state. `tofu validate` passes clean. ``` $ tofu fmt -recursive (no changes) $ tofu validate Success! The configuration is valid. ``` ## Apply order 1. `pal-e-deployments` overlay (already creates `westside-admin` namespace -- merged in #134) 2. `pal-e-platform` `make tofu-apply` (this PR -- creates the mirror) 3. The follow-up `pal-e-deployments` PR (rewires the deployment's `DATABASE_URL` to the new mirror) can merge in any order; ArgoCD will reconcile once the mirror exists. ## Test Plan - [ ] CI runs the standard plan-step cleanly (this is the live integration test for #309's plan-step shell-escaping fix) - [ ] After `make tofu-apply`: `kubectl get secret admin-app-db-url -n westside-admin -o jsonpath='{.data.DATABASE_URL}' | base64 -d` matches the same query against `-n basketball-api` - [ ] Rotation test: bump `admin_app_db_password` in Salt pillar, `make tofu-apply`, confirm both secrets show new value, then `kubectl rollout restart deployment/westside-admin -n westside-admin` ## Review Checklist - [ ] Terraform `fmt` clean, `validate` clean - [ ] No new variables, providers, or modules introduced - [ ] Source secret (`admin_app_db_url` on basketball-api) unchanged in shape -- only the value expression was extracted into a local - [ ] Mirror secret name matches source name (`admin-app-db-url`) so no consumer-side renaming is required - [ ] Namespace data source guards apply ordering with a clear failure mode - [ ] No cluster-wide controllers (kubernetes-replicator, ESO) introduced - [ ] CI plan step runs cleanly (validates #309 fix in production) ## Related Notes - Decision review: `review-135-2026-04-25` (Lucas-approved option 4) - Memory: `feedback_tofu_lock_false`, `feedback_discovered_scope_always_tracked` - Convention: cross-namespace secret distribution via terraform mirror (no replicator) ## Related - Closes forgejo_admin/pal-e-deployments#135 - Builds on: pal-e-platform PR #304 (admin_app role + source secret), PR #308 (Salt pillar entry) - Follow-up: `forgejo_admin/pal-e-deployments` branch `135-drop-database-url-sops` (drops SOPS placeholder + rewires `secretKeyRef`) - story:admin-row-crud - arch:postgres, arch:k8s-deploy 🤖 Generated with [Claude Code](https://claude.com/claude-code)
database: mirror admin-app-db-url into westside-admin namespace
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
140cb604dd
Add a second kubernetes_secret_v1 in the westside-admin namespace that
mirrors the admin-app-db-url secret already created on basketball-api by
PR #304. Both secrets derive DATABASE_URL from a single
local.admin_app_database_url expression backed by var.admin_app_db_password,
so a Salt-pillar password rotation + tofu apply propagates to BOTH
namespaces in one step.

Decision per scope review on #135 (Lucas-approved 2026-04-25): option 4
(terraform mirror beside the source). Rejected cluster-wide replicators
(kubernetes-replicator, ESO) and SOPS duplication.

Apply ordering:
  pal-e-deployments overlay (creates westside-admin ns) ->
  pal-e-platform `make tofu-apply`

A data "kubernetes_namespace_v1" "westside_admin" guards the mirror so
apply fails fast with a clear message if the overlay has not been applied
yet.

Consumer pod-roll after rotation:
  kubectl rollout restart deployment/westside-admin -n westside-admin

story:admin-row-crud
arch:postgres,k8s-deploy

Closes forgejo_admin/pal-e-deployments#135

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Contributor

PR #310 Review

DOMAIN REVIEW (Terraform / k8s secrets)

Single source of truth: Verified. local.admin_app_database_url is defined once (main.tf:291-293) and referenced by BOTH kubernetes_secret_v1.admin_app_db_url (line 259) and kubernetes_secret_v1.admin_app_db_url_westside_admin (line 314). A var.admin_app_db_password rotation propagates atomically to both namespaces in a single tofu apply. No string duplication.

Source secret unchanged in shape: The basketball-api secret's metadata, labels, name, key, and depends_on are identical to PR #304's resource. The only delta is the data value being read from a local instead of an inline expression — semantically equivalent, no resource recreation expected (Terraform should show a no-op or in-place update on the data block).

Namespace dependency soundness: data "kubernetes_namespace_v1" "westside_admin" is referenced via metadata[0].name in the mirror's namespace field — implicit dependency is correct. Apply will fail fast with the standard kubernetes provider "namespaces 'westside-admin' not found" error if the deployments overlay hasn't created the ns. Acceptable gate; the PR body documents the apply ordering.

No replicator / ESO / cluster-wide controllers: Confirmed. Pure native kubernetes_secret_v1 × 2.

No source modification at deployment level: Confirmed (this PR doesn't touch deployments; sibling PR #136 handles the secretKeyRef rewire).

Labels: Mirror carries mirror-of: basketball-api/admin-app-db-url plus the standard managed-by, story, arch labels. Good operator visibility.

Outputs: admin_app_db_url_secret_name and admin_app_db_url_namespaces are well-scoped diagnostic outputs; the namespaces list will surface drift if either resource ever moves.

Tofu hygiene: Diff is tofu fmt-clean (alignment is consistent with the rest of the file). PR body asserts tofu validate passes; no syntax red flags on read.

CI / meta-fix integration test (#309): Pipeline #456 (push event, branch 135-cross-ns-secret-mirror) returned status: success. No unterminated quoted string regression. The #309 Woodpecker shell-escape fix is verified general against this terraform-touching diff. No PR-event pipeline is configured for this repo (push is the integration signal). Note: the curated step list returned only clone — full plan-step output not surfaced here, but the overall pipeline status is success and matches the Forgejo combined commit status.

BLOCKERS

None.

NITS

  • The legacy comment at main.tf:242-246 (above the source secret) still hints at "Track B will likely use a kustomize secretGenerator referencing the same value or a small replicator." Now that option-4 has shipped, that speculation is stale. Worth tightening to "Mirrored into westside-admin by admin_app_db_url_westside_admin below" in a follow-up. Non-blocking.
  • outputs.tf admin_app_db_url_namespaces order is positional (basketball-api first, westside-admin second). Operators reading the output won't know which is which without reading the resource defs. A map keyed by namespace label (e.g. { source = ..., mirror = ... }) would be slightly more self-documenting. Non-blocking.
  • The PR body's Test Plan checkboxes are all unchecked. Once make tofu-apply runs and the post-apply kubectl base64 comparison is done, the boxes should be ticked or the verification noted in a comment. (Validation-pipeline concern, not a code concern.)

SOP COMPLIANCE

  • Branch named 135-cross-ns-secret-mirror — matches {issue-number}-{kebab-case} convention
  • PR body has Summary, Changes, Test Plan, Related sections
  • Related references forgejo_admin/pal-e-deployments#135, story:admin-row-crud, arch:postgres, arch:k8s-deploy
  • No secrets / .env / credentials in diff (password sourced from var.admin_app_db_password Salt pillar)
  • No scope creep — strictly two files, both module-local
  • Commit message descriptive, references decision date and rejected alternatives
  • Decision review note review-135-2026-04-25 referenced
  • tofu fmt / tofu validate reported clean (caveat: tofu plan deferred to CI per missing local k3s.tfvars — acceptable)

PROCESS OBSERVATIONS

  • Clean DORA win: small diff (69+/1-), single-resource scope, clear apply-ordering documentation, fast CI signal. This is the shape of a low-risk DF-positive change.
  • Meta-fix integration test for #309 is now confirmed: a terraform-modifying PR with non-trivial multi-line locals and a data source flowed through the plan step without shell-escape regression. Promote this to evidence on #309's validation note.
  • Cross-repo coordination is well-documented: the apply-ordering section makes it clear that pal-e-deployments overlay #134 must land first, that this PR creates the mirror, and that PR #136 (rewire) can merge in any order. ArgoCD drift after rotation is appropriately handled by the documented kubectl rollout restart.

VERDICT: APPROVED

## PR #310 Review ### DOMAIN REVIEW (Terraform / k8s secrets) **Single source of truth:** Verified. `local.admin_app_database_url` is defined once (main.tf:291-293) and referenced by BOTH `kubernetes_secret_v1.admin_app_db_url` (line 259) and `kubernetes_secret_v1.admin_app_db_url_westside_admin` (line 314). A `var.admin_app_db_password` rotation propagates atomically to both namespaces in a single `tofu apply`. No string duplication. **Source secret unchanged in shape:** The basketball-api secret's metadata, labels, name, key, and `depends_on` are identical to PR #304's resource. The only delta is the `data` value being read from a `local` instead of an inline expression — semantically equivalent, no resource recreation expected (Terraform should show a no-op or in-place update on the data block). **Namespace dependency soundness:** `data "kubernetes_namespace_v1" "westside_admin"` is referenced via `metadata[0].name` in the mirror's namespace field — implicit dependency is correct. Apply will fail fast with the standard kubernetes provider "namespaces 'westside-admin' not found" error if the deployments overlay hasn't created the ns. Acceptable gate; the PR body documents the apply ordering. **No replicator / ESO / cluster-wide controllers:** Confirmed. Pure native `kubernetes_secret_v1` × 2. **No source modification at deployment level:** Confirmed (this PR doesn't touch deployments; sibling PR #136 handles the secretKeyRef rewire). **Labels:** Mirror carries `mirror-of: basketball-api/admin-app-db-url` plus the standard `managed-by`, `story`, `arch` labels. Good operator visibility. **Outputs:** `admin_app_db_url_secret_name` and `admin_app_db_url_namespaces` are well-scoped diagnostic outputs; the namespaces list will surface drift if either resource ever moves. **Tofu hygiene:** Diff is `tofu fmt`-clean (alignment is consistent with the rest of the file). PR body asserts `tofu validate` passes; no syntax red flags on read. **CI / meta-fix integration test (#309):** Pipeline #456 (push event, branch `135-cross-ns-secret-mirror`) returned `status: success`. No `unterminated quoted string` regression. The #309 Woodpecker shell-escape fix is verified general against this terraform-touching diff. No PR-event pipeline is configured for this repo (push is the integration signal). Note: the curated step list returned only `clone` — full plan-step output not surfaced here, but the overall pipeline status is success and matches the Forgejo combined commit status. ### BLOCKERS None. ### NITS - The legacy comment at main.tf:242-246 (above the source secret) still hints at "Track B will likely use a kustomize secretGenerator referencing the same value or a small replicator." Now that option-4 has shipped, that speculation is stale. Worth tightening to "Mirrored into westside-admin by `admin_app_db_url_westside_admin` below" in a follow-up. Non-blocking. - `outputs.tf` `admin_app_db_url_namespaces` order is positional (basketball-api first, westside-admin second). Operators reading the output won't know which is which without reading the resource defs. A `map` keyed by namespace label (e.g. `{ source = ..., mirror = ... }`) would be slightly more self-documenting. Non-blocking. - The PR body's Test Plan checkboxes are all unchecked. Once `make tofu-apply` runs and the post-apply kubectl base64 comparison is done, the boxes should be ticked or the verification noted in a comment. (Validation-pipeline concern, not a code concern.) ### SOP COMPLIANCE - [x] Branch named `135-cross-ns-secret-mirror` — matches `{issue-number}-{kebab-case}` convention - [x] PR body has Summary, Changes, Test Plan, Related sections - [x] Related references `forgejo_admin/pal-e-deployments#135`, `story:admin-row-crud`, `arch:postgres`, `arch:k8s-deploy` - [x] No secrets / `.env` / credentials in diff (password sourced from `var.admin_app_db_password` Salt pillar) - [x] No scope creep — strictly two files, both module-local - [x] Commit message descriptive, references decision date and rejected alternatives - [x] Decision review note `review-135-2026-04-25` referenced - [x] `tofu fmt` / `tofu validate` reported clean (caveat: `tofu plan` deferred to CI per missing local `k3s.tfvars` — acceptable) ### PROCESS OBSERVATIONS - Clean DORA win: small diff (69+/1-), single-resource scope, clear apply-ordering documentation, fast CI signal. This is the shape of a low-risk DF-positive change. - Meta-fix integration test for #309 is now confirmed: a terraform-modifying PR with non-trivial multi-line locals and a data source flowed through the plan step without shell-escape regression. Promote this to evidence on #309's validation note. - Cross-repo coordination is well-documented: the apply-ordering section makes it clear that pal-e-deployments overlay #134 must land first, that this PR creates the mirror, and that PR #136 (rewire) can merge in any order. ArgoCD drift after rotation is appropriately handled by the documented `kubectl rollout restart`. ### VERDICT: APPROVED
forgejo_admin deleted branch 135-cross-ns-secret-mirror 2026-04-28 04:35:27 +00:00
Sign in to join this conversation.
No description provided.