chore(asset): add postgres bootstrap and pg_ident template
Idempotent SQL for role and database creation, split between the
postgres-database scope (bootstrap.sql) and the moments-database
scope (bootstrap-moments.sql), since CREATE DATABASE can't run
inside a DO block or transaction.
Roles:
moments_rw — owner of the moments database; runs migrations
and writes events from moments-worker.
moments_ro — read-only; consumed by moments-api.
The pg_ident template is rendered per-host by deploy.sh once it
lands; one (host, role) mapping per file. Reload required on both
magrathea and frankie after install — pg_ident is not replicated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
11
asset/postgres/ident.conf.tmpl
Normal file
11
asset/postgres/ident.conf.tmpl
Normal file
@@ -0,0 +1,11 @@
|
||||
# moments — pg_ident.conf.d drop-in template
|
||||
# Rendered by script/deploy.sh per host based on manifest.yml.
|
||||
# Install path: /var/lib/pgsql/18/data/pg_ident.conf.d/{{HOST_FQDN}}.conf
|
||||
# Apply with: sudo systemctl reload postgresql-18 — on BOTH magrathea
|
||||
# (primary) and frankie (standby) so failover doesn't lock the app out.
|
||||
#
|
||||
# One line per (host, role) mapping. A host that runs both moments-api
|
||||
# and moments-worker will have two lines (one for moments_ro, one for
|
||||
# moments_rw).
|
||||
|
||||
cert_cn {{HOST_FQDN}} {{DB_ROLE}}
|
||||
17
asset/sql/bootstrap-moments.sql
Normal file
17
asset/sql/bootstrap-moments.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- In-database grants for the moments database.
|
||||
-- Run after asset/sql/bootstrap.sql, against the moments database.
|
||||
-- Idempotent — safe to re-run on every deploy.
|
||||
--
|
||||
-- psql -h magrathea.kosherinata.internal -U postgres -d moments \
|
||||
-- -f asset/sql/bootstrap-moments.sql
|
||||
--
|
||||
-- The schema itself is created by sqlx migrations executed by moments-api
|
||||
-- on startup (which runs as moments_rw, the database owner). This file
|
||||
-- only manages the read-only role's access to whatever moments_rw creates.
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO moments_ro;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO moments_ro;
|
||||
|
||||
-- Tables created later by moments_rw (running migrations) inherit SELECT for moments_ro.
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE moments_rw IN SCHEMA public
|
||||
GRANT SELECT ON TABLES TO moments_ro;
|
||||
26
asset/sql/bootstrap.sql
Normal file
26
asset/sql/bootstrap.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- moments role and database bootstrap.
|
||||
-- Run as a postgres superuser against the cluster's `postgres` database.
|
||||
-- Idempotent — safe to re-run on every deploy.
|
||||
--
|
||||
-- psql -h magrathea.kosherinata.internal -U postgres -d postgres \
|
||||
-- -f asset/sql/bootstrap.sql
|
||||
--
|
||||
-- After this completes, run asset/sql/bootstrap-moments.sql against the
|
||||
-- newly created `moments` database to apply the in-database grants.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'moments_rw') THEN
|
||||
CREATE ROLE moments_rw LOGIN;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'moments_ro') THEN
|
||||
CREATE ROLE moments_ro LOGIN;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
SELECT 'CREATE DATABASE moments OWNER moments_rw'
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'moments')
|
||||
\gexec
|
||||
|
||||
GRANT CONNECT ON DATABASE moments TO moments_ro, moments_rw;
|
||||
Reference in New Issue
Block a user