Skip to content

RV-S1: align Terraform, deployer and entrypoint on the prod database (#172) - #211

Closed
amar-python wants to merge 1 commit into
mainfrom
fix/172-prod-db-naming
Closed

amar-python wants to merge 1 commit into
mainfrom
fix/172-prod-db-naming

Conversation

@amar-python

Copy link
Copy Markdown
Owner

Closes #172.

Decision

Canonical production database: te_prod, owned by pgadmin (option (a); name chosen by the product owner on 9 Nov). Recorded in DECISIONS/2026-11-09-prod-db-naming.md.

What was wrong, and what changed

Artefact Before After
infra/terraform-prod te_prod, admin pgadmin unchanged (already right); comment ties it to the launcher; adds the app-role password secret
build/deploy_all.sh te_mgmt_prod, OWNER = postgres, hardcoded reads db_name and db_owner from the launcher it runs
infra/entrypoint.sh needed an env_prod.sql nothing in a clean image creates generates it at run time from the image's .example.sql template

The root cause of the drift was that the deployer kept its own copy of the name and owner. db_owner was declared in every launcher but never read by anything. The launcher is now the single statement of both, and tests/test_prod_db_naming.py fails if Terraform, the launcher, the deployer or the entrypoint disagree.

Entrypoint (per the issue's "preferred approach"; no populated launcher is ever committed or baked, so #174 is not reintroduced):

  • builds env_<env>.sql from the template + APP_USER_PASSWORD, mode 0600, deleted on exit;
  • refuses to run without APP_USER_PASSWORD, naming it, rather than creating te_prod_user with the template's placeholder password;
  • refuses if PGDATABASE is not the launcher's db_name, before touching anything. This is the exact Terraform/deployer drift, now caught at run time.

Deployer: if the owner role does not exist (CI, dev server, offline install) it creates it NOLOGIN, so it owns the database and adds no way in. On Azure it is the server admin and already exists. An existing database with a different owner is reported, never altered.

Terraform: a Key Vault secret pg-app-user-password and the job's APP_USER_PASSWORD env, mirroring the existing pg_password pattern. Without it the first Azure deploy would fail at the entrypoint with the new, clear error.

Consumers of the old name updated so nothing breaks: CI (the database-creation step now makes te_prod owned by pgadmin, as Azure has it), evals Tier E, test_parity, test_env_prod, run_tests.sh, config examples, provision_full_test_env.sh, the offline installer and the docs. I also fixed the Dockerfile's default PGDATABASE=te_dev, which is a schema name, not a database; the new guard would otherwise reject every dev run.

Verification (the issue's "Done when")

No Docker daemon was available. I emulated the image faithfully instead: the tree comes from git archive of the commit (committed files only, so no gitignored launcher or config.local.env), laid out as infra/Dockerfile does at /opt/migration, run as the non-root app user (uid 1001). Servers are brand-new PostgreSQL 16 clusters with scram-sha-256 auth and no role setup.

Baseline, unfixed origin/main (74c248f), Azure-shaped server (only role pgadmin), te_prod pre-created as Terraform does:

[✗] Environment file not found: /opt/migration/build/environments/env_prod.sql
[✗] Failed    : prod            exit code: 1

Fixed (5c9b930), same server:

[✓] Environment PROD deployed successfully.        exit code: 0
launcher left in image: []
databases: te_prod(owner pgadmin)
roles: pgadmin,te_prod_user                        <- no 'postgres' role exists on this server
tables in schema te_prod: 12
app role logs in with the awkward password: yes, 12 tables visible

The password used was a'b\c&d/e"f g!, chosen to break naive quoting.

Case Result
Azure shape, te_prod pre-created exit 0, owner pgadmin
Second run exit 0, "already exists — skipping CREATE", no ownership warning
Azure shape, database absent exit 0, Creating database: te_prod (owner: pgadmin)
Server with only postgres (no pgadmin) exit 0, pgadmin(nologin) created, owner pgadmin
No APP_USER_PASSWORD exit 1, ERROR: APP_USER_PASSWORD is not set…
PGDATABASE=te_mgmt_prod exit 1, PGDATABASE is 'te_mgmt_prod' but env_prod.sql deploys to 'te_prod'; nothing created

terraform validate passes on infra/terraform-prod (Terraform 1.9.8, installed from a checksum-verified download). terraform plan was not run: it needs Azure credentials.

CI replica

Run on a live PostgreSQL 16, in CI's order on freshly created databases:

evals tiers p,i,s,e                 total 26 / passed 26 / failed 0 / skipped 0
deploy all four environments        all deployed, no ownership warning
full suite (--strict)               PASSED 378 / FAILED 0 / ERROR 0 / SKIPPED 0 / NOT RUN 0
make lint                           flake8 clean, bandit clean
make health                         88 passed, 0 warnings, 0 failed

tests/test_prod_db_naming.py adds 13 tests: 8 static (free-tier job) and 5 that run the real entrypoint (integration job). On unfixed code 10 failed and 3 passed.

Not cited here: an earlier local run showed 1 Tier-S failure (A01 — Row count = 5, got 32). That was my own te_dev data left by earlier pytest runs; it passes on fresh databases, as above and as in CI.

Please read before merging


Generated by Claude Code

…172)

Terraform created te_prod for pgadmin; build/deploy_all.sh created
te_mgmt_prod for postgres, hardcoded; infra/entrypoint.sh needed an
env_prod.sql that a freshly built image does not contain, so a first
deploy would stop at "Environment file not found".

Canonical (product owner, 9 Nov): database te_prod, owner pgadmin.

- build/environments/env_prod.example.sql is the single statement of
  db_name and db_owner. deploy_all.sh now reads both from the launcher it
  runs instead of keeping its own copy, which is what drifted. db_owner was
  declared in every launcher but never used.
- deploy_all.sh creates the owner role NOLOGIN when it does not exist (CI,
  dev servers, the offline install; on Azure it is the server admin), and
  reports, but does not change, an existing database with another owner.
- infra/entrypoint.sh generates env_<env>.sql at run time from the image's
  .example.sql template and APP_USER_PASSWORD, writes it 0600, and deletes it
  on exit. The image still holds no launcher with a password (RV-S4, #174).
  It refuses to run without APP_USER_PASSWORD, and refuses when PGDATABASE is
  not the launcher's db_name, before changing anything.
- infra/terraform-prod: Key Vault secret and job wiring for APP_USER_PASSWORD,
  mirroring pg_password; comment tying the database to the launcher.
- Consumers of the old name follow: CI, evals Tier E, parity and env tests,
  config examples, provisioning script, offline installer, docs. CI now
  creates te_prod owned by pgadmin, as Azure has it.
- Dockerfile default PGDATABASE was te_dev, a schema name, not a database.

tests/test_prod_db_naming.py: static checks that the artefacts agree, and a
behavioural run of the real entrypoint from committed files only.

DECISIONS/2026-11-09-prod-db-naming.md records the choice. Test and staging
have the same Terraform/deployer disagreement and are not changed here.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011T6WqjTEDRRQGLEYPPFVXx

Copy link
Copy Markdown
Owner Author

Closing in favour of #213, which implements the same fix (#172) more completely. Nothing here is lost: the branch fix/172-prod-db-naming is kept, so this can be reopened.

Why

Independent check of #213 (head 0a0869f): clean-image layout, run as uid 1001, against a brand-new PostgreSQL 16 whose only role is pgadmin, with te_prod pre-created as Terraform does: exit 0, owner pgadmin, 12 tables, roles pgadmin and te_prod_user only, the password appears 0 times in the output and nowhere on disk, and the application role authenticates with a password containing ' \ & / ".

Closed by Claude at the owner's request.


Generated by Claude Code

@amar-python amar-python closed this Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RV-S1: Terraform / deployer / entrypoint disagree on production DB name and owner

2 participants