Freeze lab-tested PostgreSQL plugin engine
Extract PostgreSQL, Podman runtime and systemd lifecycle adapters while preserving existing lifecycle and scoring behavior. Update RPM layout and add guarded operator-run lab harnesses. Record successful manual stop/start, promotion, demotion and rewind-only rejoin with pinned installed hashes. The unfenced lab does not validate automatic failover, Pacemaker-managed transitions or divergent-data rewind. Validation: 103 offline tests, original-function differential checks, checksum manifest and whitespace checks passed.
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
# Sourced by managed-container; uses the shared OCF and parameter environment.
|
||||
|
||||
pg_remote_query()
|
||||
{
|
||||
local sql="$1"
|
||||
|
||||
pg_replication_exec \
|
||||
psql \
|
||||
"host=${REJOIN_SOURCE_HOST} port=${REJOIN_SOURCE_PORT} dbname=postgres user=${REPLICATION_USER}" \
|
||||
-Atqc "${sql}"
|
||||
}
|
||||
|
||||
ensure_local_ha_slots()
|
||||
{
|
||||
local slots raw_slot slot slot_type
|
||||
|
||||
#
|
||||
# POSTGRES_HA_SLOTS lives in the container environment supplied by
|
||||
# postgres.env. It is the cluster-wide list of physical slots that
|
||||
# every writable primary must provide.
|
||||
#
|
||||
slots="$(
|
||||
pg_command '
|
||||
printf "%s" "${POSTGRES_HA_SLOTS:-}"
|
||||
' 2>/dev/null
|
||||
)" || {
|
||||
ocf_exit_reason \
|
||||
"Unable to read POSTGRES_HA_SLOTS from PostgreSQL container"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
}
|
||||
|
||||
if [ -z "${slots}" ]; then
|
||||
ocf_log warn \
|
||||
"POSTGRES_HA_SLOTS is empty; no local HA slots will be reconciled"
|
||||
return "${OCF_SUCCESS}"
|
||||
fi
|
||||
|
||||
while IFS= read -r raw_slot; do
|
||||
slot="$(printf '%s' "${raw_slot}" | tr -d '[:space:]')"
|
||||
|
||||
[ -n "${slot}" ] || continue
|
||||
|
||||
case "${slot}" in
|
||||
*[!a-z0-9_]*)
|
||||
ocf_exit_reason \
|
||||
"Invalid physical replication slot name: ${slot}"
|
||||
return "${OCF_ERR_CONFIGURED}"
|
||||
;;
|
||||
esac
|
||||
|
||||
slot_type="$(
|
||||
pg_command "
|
||||
psql \
|
||||
-U '${DB_USER}' \
|
||||
-d postgres \
|
||||
-At \
|
||||
-v ON_ERROR_STOP=1 \
|
||||
-c \"
|
||||
SELECT slot_type
|
||||
FROM pg_replication_slots
|
||||
WHERE slot_name = '${slot}';
|
||||
\"
|
||||
" 2>/dev/null
|
||||
)" || {
|
||||
ocf_exit_reason \
|
||||
"Failed querying replication slot ${slot}"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
}
|
||||
|
||||
slot_type="$(printf '%s' "${slot_type}" | tr -d '[:space:]')"
|
||||
|
||||
case "${slot_type}" in
|
||||
physical)
|
||||
ocf_log info \
|
||||
"Physical HA slot already exists: ${slot}"
|
||||
;;
|
||||
|
||||
"")
|
||||
ocf_log info \
|
||||
"Creating physical HA slot: ${slot}"
|
||||
|
||||
if ! pg_command "
|
||||
psql \
|
||||
-U '${DB_USER}' \
|
||||
-d postgres \
|
||||
-v ON_ERROR_STOP=1 \
|
||||
-c \"
|
||||
SELECT pg_create_physical_replication_slot('${slot}');
|
||||
\"
|
||||
" >/dev/null
|
||||
then
|
||||
ocf_exit_reason \
|
||||
"Failed creating physical replication slot ${slot}"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
ocf_exit_reason \
|
||||
"Replication slot ${slot} exists but is type ${slot_type}"
|
||||
return "${OCF_ERR_CONFIGURED}"
|
||||
;;
|
||||
esac
|
||||
|
||||
done <<EOF
|
||||
$(printf '%s\n' "${slots}" | tr ',' '\n')
|
||||
EOF
|
||||
|
||||
return "${OCF_SUCCESS}"
|
||||
}
|
||||
|
||||
ensure_remote_slot()
|
||||
{
|
||||
local host="${1:-}"
|
||||
local slot="${2:-}"
|
||||
local result
|
||||
|
||||
[ -n "${host}" ] || return 1
|
||||
[ -n "${slot}" ] || return 1
|
||||
|
||||
result="$(
|
||||
pg_replication_exec \
|
||||
psql \
|
||||
"host=${host} port=${REJOIN_SOURCE_PORT} user=${REPLICATION_USER} dbname=postgres" \
|
||||
-Atc "
|
||||
SELECT slot_type
|
||||
FROM pg_replication_slots
|
||||
WHERE slot_name = '${slot}';
|
||||
" 2>/dev/null
|
||||
)" || return 1
|
||||
|
||||
case "${result}" in
|
||||
physical)
|
||||
return 0
|
||||
;;
|
||||
|
||||
"")
|
||||
if ! pg_replication_exec \
|
||||
psql \
|
||||
"host=${host} port=${REJOIN_SOURCE_PORT} user=${REPLICATION_USER} dbname=postgres" \
|
||||
-Atc "
|
||||
SELECT slot_name
|
||||
FROM pg_create_physical_replication_slot('${slot}');
|
||||
" >/dev/null
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
;;
|
||||
|
||||
*)
|
||||
ocf_log err \
|
||||
"Replication slot ${slot} exists on ${host} but is not physical"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
candidate_caught_up()
|
||||
{
|
||||
local candidate="${1:-}"
|
||||
local result
|
||||
|
||||
[ -n "${candidate}" ] || return 1
|
||||
|
||||
result="$(pg_command "
|
||||
psql -U '${DB_USER}' -d '${DB_NAME}' -Atc \"
|
||||
SELECT CASE
|
||||
WHEN state = 'streaming'
|
||||
AND replay_lsn >= pg_current_wal_flush_lsn()
|
||||
THEN 'yes'
|
||||
ELSE 'no'
|
||||
END
|
||||
FROM pg_stat_replication
|
||||
WHERE application_name = '${candidate}'
|
||||
LIMIT 1;
|
||||
\"
|
||||
" 2>/dev/null)" || return 1
|
||||
|
||||
[ "${result}" = "yes" ]
|
||||
}
|
||||
|
||||
candidate_is_standby()
|
||||
{
|
||||
local host="${1:-}"
|
||||
local result
|
||||
local port="${REJOIN_SOURCE_PORT:-5432}"
|
||||
|
||||
[ -n "${host}" ] || return 1
|
||||
|
||||
result="$(
|
||||
pg_replication_exec \
|
||||
psql \
|
||||
"host=${host} port=${port} user=${REPLICATION_USER} dbname=postgres" \
|
||||
-Atc "SELECT pg_is_in_recovery();" \
|
||||
2>/dev/null
|
||||
)" || return 1
|
||||
|
||||
[ "${result}" = "t" ]
|
||||
}
|
||||
Reference in New Issue
Block a user