#!/usr/bin/env bash # # Idempotently maintain the moments cert_cn → role mappings in an app-owned # pg_ident.conf.d/moments.conf on the moments postgres primary and standby, # then reload postgres so the changes take effect. Re-running is a no-op # (no duplicate lines, no spurious reload). # # The file is named for the app, not the cert_cn host, so provisioning for # other apps (which may own files for the same hosts) can never clobber or # interleave with moments access. Any moments_* lines found in legacy # host-named files are migrated here and removed from the legacy file. # # Run from a workstation with ssh access to both pg hosts. This script ssh's # out; do NOT run it on magrathea/frankie directly. set -euo pipefail api_host=nikola.kosherinata.internal worker_host=frootmig.kosherinata.internal pg_hosts=( magrathea.kosherinata.internal frankie.hanzalova.internal ) # Each (cert_cn host, role) pair becomes one cert_cn line in # pg_ident.conf.d/moments.conf on every pg host listed above. mapping_pairs=( "$api_host" moments_ro "$worker_host" moments_rw ) ident_dir=/var/lib/pgsql/18/data/pg_ident.conf.d app_file=moments.conf failed=0 for pg_host in "${pg_hosts[@]}"; do printf '==> %s\n' "$pg_host" if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$pg_host" \ "sudo bash -s -- ${ident_dir@Q} ${app_file@Q} ${mapping_pairs[@]@Q}" <<'REMOTE_EOF' set -euo pipefail ident_dir="$1"; shift file="${ident_dir}/$1"; shift changed=0 # Migrate any moments_* lines out of legacy host-named files into the # app-owned file, so other apps' provisioning (which may own files for the # same hosts) can never clobber or drop moments access. Lines are preserved # verbatim (this covers mappings beyond mapping_pairs, e.g. a workstation's). # A legacy file left empty is removed. moments_re='^cert_cn[[:space:]]+[^[:space:]]+[[:space:]]+moments_(ro|rw)[[:space:]]*$' for legacy in "$ident_dir"/*.conf; do [[ "$legacy" == "$file" ]] && continue grep --extended-regexp --quiet "$moments_re" "$legacy" || continue while read -r line; do if [[ -f "$file" ]] && grep --fixed-strings --line-regexp --quiet -- "$line" "$file"; then printf ' already in %s, dropping from %s: %s\n' "${file##*/}" "${legacy##*/}" "$line" else printf '%s\n' "$line" | sudo -u postgres tee --append "$file" >/dev/null printf ' migrated from %s: %s\n' "${legacy##*/}" "$line" fi done < <(grep --extended-regexp "$moments_re" "$legacy") remaining="$(grep --extended-regexp --invert-match "$moments_re" "$legacy" || true)" if [[ -n "$remaining" ]]; then printf '%s\n' "$remaining" | sudo -u postgres tee "$legacy" >/dev/null else rm "$legacy" printf ' removed empty legacy file %s\n' "${legacy##*/}" fi changed=1 done while [[ $# -gt 0 ]]; do cert_cn_host="$1" role="$2" shift 2 line="cert_cn ${cert_cn_host} ${role}" # The heredoc runs as root via sudo bash, so [[ -f ]] and grep are fine # without dropping privs. tee --append runs as postgres so a newly-created # file lands with the conventional postgres:postgres ownership. if [[ -f "$file" ]] && grep --fixed-strings --line-regexp --quiet -- "$line" "$file"; then printf ' present: %s\n' "$line" else printf '%s\n' "$line" | sudo -u postgres tee --append "$file" >/dev/null printf ' added: %s\n' "$line" changed=1 fi done if (( changed )); then systemctl reload postgresql-18 echo " reloaded postgresql-18" else echo " no changes; reload skipped" fi REMOTE_EOF then echo " WARNING: ${pg_host} unreachable or failed — re-run when it is back" failed=1 fi done exit "$failed"