#!/usr/bin/env bash # db-primary (10.20.0.20): PostgreSQL 16 primary for the app DB (experiment design 3.3, Terraform design 6.10). # Idempotent: package install, config, roles/schema/seed are all re-runnable. Passwords come from Secret Manager # (secret selfheal-db-password, one value shared by roles app / replicator / harness in this lab) via the VM SA. set -euo pipefail export DEBIAN_FRONTEND=noninteractive PGV=16 PRIMARY_IP=10.20.0.20; REPLICA_IP=10.20.0.21; AWS_CIDR=10.10.0.0/16; GCP_CIDR=10.20.0.0/20 apt-get update -qq apt-get install -y -qq postgresql-${PGV} jq >/dev/null PROJECT="$(curl -sf -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/project/project-id)" TOKEN="$(curl -sf -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' | jq -r .access_token)" DB_PASSWORD="$(curl -sf -H "Authorization: Bearer ${TOKEN}" "https://secretmanager.googleapis.com/v1/projects/${PROJECT}/secrets/selfheal-db-password/versions/latest:access" | jq -r .payload.data | base64 -d)" unset TOKEN [ -n "$DB_PASSWORD" ] || { echo "empty DB password" >&2; exit 1; } CONF=/etc/postgresql/${PGV}/main # --- postgresql.conf overrides (conf.d is included by the packaged postgresql.conf) --- install -d -m 755 -o postgres -g postgres "$CONF/conf.d" cat > "$CONF/conf.d/10-selfheal.conf" < "$CONF/pg_hba.conf" </dev/null systemctl restart postgresql sleep 2 # --- roles, database, schema, seed (idempotent) --- sudo -u postgres psql -v ON_ERROR_STOP=1 -v pw="$DB_PASSWORD" <<'SQL' DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='app') THEN CREATE ROLE app LOGIN; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='replicator') THEN CREATE ROLE replicator LOGIN REPLICATION; END IF; IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='harness') THEN CREATE ROLE harness LOGIN; END IF; END $$; ALTER ROLE app PASSWORD :'pw'; ALTER ROLE replicator PASSWORD :'pw'; ALTER ROLE harness PASSWORD :'pw'; -- harness: the catalogue actions db.replay_resume (replica) and db.promote (design 2.3); read access for checks GRANT EXECUTE ON FUNCTION pg_wal_replay_pause() TO harness; GRANT EXECUTE ON FUNCTION pg_wal_replay_resume() TO harness; GRANT EXECUTE ON FUNCTION pg_promote(boolean, integer) TO harness; GRANT pg_read_all_stats TO harness; SELECT 'CREATE DATABASE app OWNER app' WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname='app') \gexec SQL sudo -u postgres psql -v ON_ERROR_STOP=1 -d app <<'SQL' CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION app; CREATE TABLE IF NOT EXISTS app.price ( item text PRIMARY KEY, price numeric(10,2) NOT NULL, updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS app.orders ( id bigserial PRIMARY KEY, item text NOT NULL REFERENCES app.price(item), qty integer NOT NULL CHECK (qty > 0), status text NOT NULL DEFAULT 'new', created_at timestamptz NOT NULL DEFAULT now() ); ALTER TABLE app.price OWNER TO app; ALTER TABLE app.orders OWNER TO app; ALTER SEQUENCE app.orders_id_seq OWNER TO app; GRANT USAGE ON SCHEMA app TO harness; GRANT SELECT ON ALL TABLES IN SCHEMA app TO harness; INSERT INTO app.price (item, price) VALUES ('apple', 120.00), ('banana', 80.00), ('cherry', 450.00), ('durian', 1800.00) ON CONFLICT (item) DO NOTHING; INSERT INTO app.orders (item, qty, status) SELECT 'apple', 2, 'done' WHERE NOT EXISTS (SELECT 1 FROM app.orders); SQL unset DB_PASSWORD echo "--- primary status" sudo -u postgres psql -Atc "select version()" | cut -d, -f1 sudo -u postgres psql -Atc "show ssl" sudo -u postgres psql -d app -Atc "select count(*) from app.price"