From 20e1210441607c9b9e4d20948bcdfc8b360da6d9 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 28 Jul 2026 11:12:19 -0700 Subject: [PATCH] postgresql: add disposable dry-run test stack for cutover validation (pgha-test, own network + data dirs, zero prod impact) --- postgresql/cutover/test/pgha-dryrun.yaml | 208 +++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 postgresql/cutover/test/pgha-dryrun.yaml diff --git a/postgresql/cutover/test/pgha-dryrun.yaml b/postgresql/cutover/test/pgha-dryrun.yaml new file mode 100644 index 0000000..fae0e76 --- /dev/null +++ b/postgresql/cutover/test/pgha-dryrun.yaml @@ -0,0 +1,208 @@ +# ───────────────────────────────────────────────────────────────────────── +# postgresql/cutover/test/pgha-dryrun.yaml — DISPOSABLE dry-run stack +# +# Validates the ADR-0001 cutover mechanics WITHOUT touching production: +# - own overlay network (pgha-test_db-backend), own stack name +# - throwaway "legacy" postgres:17 seeded with marker data, carrying the +# in-network alias "postgresql" (mirrors prod CLONE_HOST) +# - same etcd/patroni/haproxy topology, images, and env wiring as the +# real staging file — including clone-from-live via pg_basebackup +# - reuses the real Docker secrets (read-only mounts; harmless) +# - disposable data dirs under /volume1/docker/PostgreSQL/dryrun/ +# +# Deploy: docker stack deploy -c pgha-test +# Teardown: docker stack rm pgha-test && rm -rf /volume1/docker/PostgreSQL/dryrun +# ───────────────────────────────────────────────────────────────────────── +version: "3.6" + +services: + + # Stand-in for the production single-instance postgres (clone source) + legacy: + image: public.ecr.aws/docker/library/postgres:17 + hostname: db + command: [postgres] + environment: + POSTGRES_USER: PGadmin + POSTGRES_PASSWORD_FILE: /run/secrets/postgresql_password + secrets: + - postgresql_password + volumes: + - /volume1/docker/PostgreSQL/dryrun/legacy:/var/lib/postgresql/data + networks: + db-backend: + aliases: + - postgresql + - db + deploy: + placement: + constraints: + - node.hostname == docker-2 + + etcd-1: + image: quay.io/coreos/etcd:v3.5.9 + hostname: etcd-1 + command: + - etcd + - --name=etcd-1 + - --data-dir=/etcd-data + - --initial-advertise-peer-urls=http://etcd-1:2380 + - --listen-peer-urls=http://0.0.0.0:2380 + - --listen-client-urls=http://0.0.0.0:2379 + - --advertise-client-urls=http://etcd-1:2379 + - --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380 + - --initial-cluster-state=new + - --initial-cluster-token=pgha-test-etcd + volumes: + - /volume1/docker/PostgreSQL/dryrun/etcd-1:/etcd-data + networks: + - db-backend + deploy: + placement: + constraints: + - node.hostname == docker-1 + + etcd-2: + image: quay.io/coreos/etcd:v3.5.9 + hostname: etcd-2 + command: + - etcd + - --name=etcd-2 + - --data-dir=/etcd-data + - --initial-advertise-peer-urls=http://etcd-2:2380 + - --listen-peer-urls=http://0.0.0.0:2380 + - --listen-client-urls=http://0.0.0.0:2379 + - --advertise-client-urls=http://etcd-2:2379 + - --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380 + - --initial-cluster-state=new + - --initial-cluster-token=pgha-test-etcd + volumes: + - /volume1/docker/PostgreSQL/dryrun/etcd-2:/etcd-data + networks: + - db-backend + deploy: + placement: + constraints: + - node.hostname == docker-2 + + etcd-3: + image: quay.io/coreos/etcd:v3.5.9 + hostname: etcd-3 + command: + - etcd + - --name=etcd-3 + - --data-dir=/etcd-data + - --initial-advertise-peer-urls=http://etcd-3:2380 + - --listen-peer-urls=http://0.0.0.0:2380 + - --listen-client-urls=http://0.0.0.0:2379 + - --advertise-client-urls=http://etcd-3:2379 + - --initial-cluster=etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380,etcd-3=http://etcd-3:2380 + - --initial-cluster-state=new + - --initial-cluster-token=pgha-test-etcd + volumes: + - /volume1/docker/PostgreSQL/dryrun/etcd-3:/etcd-data + networks: + - db-backend + deploy: + placement: + constraints: + - node.hostname == docker-3 + + patroni-0: + image: ghcr.io/zalando/spilo-17:4.0-p3 + hostname: patroni-0 + command: + - /bin/sh + - -c + - | + export PGPASSWORD_SUPERUSER="$(cat /run/secrets/postgresql_password)" + export PGPASSWORD_STANDBY="$(cat /run/secrets/postgresql_replication_password)" + export PATRONI_RESTAPI_PASSWORD="$(cat /run/secrets/postgresql_patroni_password)" + export CLONE_PASSWORD="$(cat /run/secrets/postgresql_password)" + exec /bin/sh /launch.sh init + environment: + SCOPE: pgha-test + PATRONI_NAME: patroni-0 + ETCD_HOSTS: '"etcd-1:2379","etcd-2:2379","etcd-3:2379"' + PGUSER_SUPERUSER: PGadmin + PGUSER_STANDBY: standby + PATRONI_RESTAPI_USERNAME: patroni + PGROOT: /home/postgres/pgdata/pgroot + CLONE_METHOD: CLONE_WITH_BASEBACKUP + CLONE_SCOPE: legacy-single + CLONE_HOST: postgresql + CLONE_PORT: "5432" + CLONE_USER: PGadmin + secrets: + - postgresql_password + - postgresql_replication_password + - postgresql_patroni_password + volumes: + - /volume1/docker/PostgreSQL/dryrun/patroni-0:/home/postgres/pgdata + networks: + - db-backend + deploy: + placement: + constraints: + - node.labels.pg-role == primary + + patroni-1: + image: ghcr.io/zalando/spilo-17:4.0-p3 + hostname: patroni-1 + command: + - /bin/sh + - -c + - | + export PGPASSWORD_SUPERUSER="$(cat /run/secrets/postgresql_password)" + export PGPASSWORD_STANDBY="$(cat /run/secrets/postgresql_replication_password)" + export PATRONI_RESTAPI_PASSWORD="$(cat /run/secrets/postgresql_patroni_password)" + export CLONE_PASSWORD="$(cat /run/secrets/postgresql_password)" + exec /bin/sh /launch.sh init + environment: + SCOPE: pgha-test + PATRONI_NAME: patroni-1 + ETCD_HOSTS: '"etcd-1:2379","etcd-2:2379","etcd-3:2379"' + PGUSER_SUPERUSER: PGadmin + PGUSER_STANDBY: standby + PATRONI_RESTAPI_USERNAME: patroni + PGROOT: /home/postgres/pgdata/pgroot + CLONE_METHOD: CLONE_WITH_BASEBACKUP + CLONE_SCOPE: legacy-single + CLONE_HOST: postgresql + CLONE_PORT: "5432" + CLONE_USER: PGadmin + secrets: + - postgresql_password + - postgresql_replication_password + - postgresql_patroni_password + volumes: + - /volume1/docker/PostgreSQL/dryrun/patroni-1:/home/postgres/pgdata + networks: + - db-backend + deploy: + placement: + constraints: + - node.labels.pg-role == replica + + haproxy: + image: haproxy:2.9-alpine + hostname: haproxy + volumes: + - /volume1/docker/compose-files/postgresql/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + networks: + - db-backend + deploy: + mode: global + +networks: + db-backend: + attachable: true + driver: overlay + +secrets: + postgresql_password: + external: true + postgresql_replication_password: + external: true + postgresql_patroni_password: + external: true