From dc6120ffd4b74ff7a143c0bd4d08068f97b92847 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 30 Jul 2026 21:44:49 -0700 Subject: [PATCH] pgha-dryrun.yaml: trigger secret re-provisioning after regenerating postgresql_replication_password (excludes &<>\" per pystache HTML-escaping bug found this session) --- postgresql/cutover/test/pgha-dryrun.yaml | 44 +++++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/postgresql/cutover/test/pgha-dryrun.yaml b/postgresql/cutover/test/pgha-dryrun.yaml index e775f52..7e5e56e 100644 --- a/postgresql/cutover/test/pgha-dryrun.yaml +++ b/postgresql/cutover/test/pgha-dryrun.yaml @@ -1,6 +1,13 @@ # ───────────────────────────────────────────────────────────────────────── # postgresql/cutover/test/pgha-dryrun.yaml — DISPOSABLE dry-run stack # +# TRIGGER NOTE (2026-08-01): touched to force Woodpecker to re-run secret +# provisioning for the `postgresql` stack case after regenerating +# postgresql_replication_password to exclude &<>" (see ADR-0001 Dry-Run +# Debugging Log note for context — Spilo's pystache config templating +# HTML-escapes double-brace {{PGPASSWORD_STANDBY}} substitutions, corrupting +# any password containing those characters). +# # 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 @@ -61,23 +68,42 @@ # docs example (that name assumes a plain Patroni method map without # Spilo's wrapper naming). # +# BUG FOUND DURING THIS DRY RUN (2026-08-01, unrelated to standby_cluster +# itself, but discovered while testing it): Spilo's pystache templating +# engine renders `password: '{{PGPASSWORD_STANDBY}}'` using DOUBLE braces +# in its TEMPLATE constant (confirmed from configure_spilo.py source) — +# pystache HTML-escapes double-brace substitutions by default (only +# triple-brace {{{...}}} skips escaping, which Spilo deliberately uses for +# archive_command elsewhere in the same template for exactly this reason). +# This means any password containing &, <, >, or " gets corrupted into +# HTML entities (e.g. & becomes &) inside Patroni's own rendered +# /run/postgres.yml AND /run/postgresql/pgpass — breaking real +# password-based Patroni-to-Patroni replication auth (confirmed via direct +# file inspection: the raw secret was verified clean via `cat | xxd`, but +# the rendered config had "&" baked in). This surfaced here specifically +# because standby_cluster's cascade-replica bootstrap (patroni-0 replicating +# from patroni-1) uses genuine hostssl+md5 auth, unlike legacy's disposable +# "trust" rule which never actually checks the password. Fix: regenerate +# postgresql_replication_password to exclude &<>" entirely — done +# 2026-08-01, this file re-tests with the corrected secret. +# # OPEN QUESTIONS THIS DRY RUN IS DESIGNED TO ANSWER (do not assume the # answer — observe actual logs): # - Does Patroni require a pre-created replication slot on "legacy" for # the standby_cluster link (primary_slot_name), or does it work -# without one since we haven't set that key? Docs say slots are only -# required "if you use replication slots" for this link — ambiguous -# whether that's opt-in or on-by-default given use_slots:true is set -# cluster-wide by Spilo's own template for OTHER purposes. +# without one since we haven't set that key? ANSWERED (first attempt, +# pre-password-fix): works fine without one — patroni-1 successfully +# bootstrapped as standby leader and began streaming from legacy with +# no slot configured. # - Does "legacy" (vanilla, unmanaged postgres:17) actually need # postgresql.conf discoverable in PGDATA for Patroni's remote checks? -# (Docs: "Patroni expects to find postgresql.conf or -# postgresql.conf.backup in PGDATA of the remote primary" — vanilla -# images keep it there by default, expected to be fine, but confirm.) +# ANSWERED: yes, works fine — vanilla image keeps it there by default, +# no complaint logged. # - Does promotion off the standby cluster (detaching from "legacy" and # becoming a normal read-write cluster) work cleanly and pick up the -# very latest streamed WAL, closing the gap as intended? This is the -# actual feature under test — Step 7c below. +# very latest streamed WAL, closing the gap as intended? STILL TO TEST +# — this is the actual feature under test, blocked behind the password +# bug above on the first attempt, retesting now. # # ─── Fixes still carried forward from the CLONE_WITH_BASEBACKUP dry run ─── #