Add PostgreSQL HA OCF agent modular baseline
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
# Sourced by managed-container; uses the shared OCF and parameter environment.
|
||||
|
||||
pg_command()
|
||||
{
|
||||
local command="$1"
|
||||
|
||||
as_service_user \
|
||||
podman exec \
|
||||
--user "${PG_OS_USER}" \
|
||||
"${CONTAINER_NAME}" \
|
||||
sh -c "${command}"
|
||||
}
|
||||
|
||||
pgdata_path()
|
||||
{
|
||||
pg_command '
|
||||
if [ -z "$PGDATA" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "%s\n" "$PGDATA"
|
||||
'
|
||||
}
|
||||
|
||||
pg_running()
|
||||
{
|
||||
pg_command '
|
||||
BINDIR="$(pg_config --bindir)" || exit 1
|
||||
"$BINDIR/pg_ctl" status -D "$PGDATA" >/dev/null 2>&1
|
||||
'
|
||||
}
|
||||
|
||||
pg_ready()
|
||||
{
|
||||
as_service_user \
|
||||
podman exec \
|
||||
--user "${PG_OS_USER}" \
|
||||
"${CONTAINER_NAME}" \
|
||||
pg_isready -q \
|
||||
-U "${DB_USER}" \
|
||||
-d "${DB_NAME}"
|
||||
}
|
||||
|
||||
pg_role()
|
||||
{
|
||||
as_service_user \
|
||||
podman exec \
|
||||
--user "${PG_OS_USER}" \
|
||||
"${CONTAINER_NAME}" \
|
||||
psql \
|
||||
-U "${DB_USER}" \
|
||||
-d "${DB_NAME}" \
|
||||
-Atqc 'SELECT pg_is_in_recovery();'
|
||||
}
|
||||
|
||||
pg_wal_receiver_status()
|
||||
{
|
||||
as_service_user \
|
||||
podman exec \
|
||||
--user "${PG_OS_USER}" \
|
||||
"${CONTAINER_NAME}" \
|
||||
psql \
|
||||
-U "${DB_USER}" \
|
||||
-d "${DB_NAME}" \
|
||||
-Atqc "SELECT status FROM pg_stat_wal_receiver LIMIT 1;"
|
||||
}
|
||||
|
||||
managed_monitor()
|
||||
{
|
||||
local role
|
||||
|
||||
if ! service_running; then
|
||||
ocf_log debug "User service ${SERVICE_NAME} is not active"
|
||||
return "${OCF_NOT_RUNNING}"
|
||||
fi
|
||||
|
||||
if ! container_running; then
|
||||
ocf_log warn \
|
||||
"Service ${SERVICE_NAME} is active but container ${CONTAINER_NAME} is not running"
|
||||
return "${OCF_NOT_RUNNING}"
|
||||
fi
|
||||
|
||||
if ! pg_running; then
|
||||
ocf_log debug \
|
||||
"Container ${CONTAINER_NAME} is running but PostgreSQL is stopped"
|
||||
return "${OCF_NOT_RUNNING}"
|
||||
fi
|
||||
|
||||
if ! pg_ready; then
|
||||
ocf_exit_reason \
|
||||
"PostgreSQL process exists but pg_isready failed"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
|
||||
role="$(pg_role 2>/dev/null)" || {
|
||||
ocf_exit_reason \
|
||||
"PostgreSQL is accepting connections but role query failed"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
}
|
||||
|
||||
case "${role}" in
|
||||
f)
|
||||
ocf_log debug \
|
||||
"PostgreSQL healthy: role=primary (promoted)"
|
||||
return "${OCF_RUNNING_PROMOTED}"
|
||||
;;
|
||||
t)
|
||||
ocf_log debug \
|
||||
"PostgreSQL healthy: role=standby (unpromoted)"
|
||||
return "${OCF_SUCCESS}"
|
||||
;;
|
||||
*)
|
||||
ocf_exit_reason \
|
||||
"Unexpected pg_is_in_recovery() result: '${role}'"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
managed_start()
|
||||
{
|
||||
local i monitor_rc
|
||||
|
||||
managed_monitor
|
||||
monitor_rc=$?
|
||||
|
||||
case "${monitor_rc}" in
|
||||
"${OCF_SUCCESS}")
|
||||
ocf_log info \
|
||||
"PostgreSQL resource already running unpromoted"
|
||||
return "${OCF_SUCCESS}"
|
||||
;;
|
||||
|
||||
"${OCF_RUNNING_PROMOTED}")
|
||||
ocf_exit_reason \
|
||||
"Refusing start: PostgreSQL is already primary; promotion must occur through promote()"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
;;
|
||||
|
||||
"${OCF_NOT_RUNNING}")
|
||||
# Expected for a stopped resource. Continue.
|
||||
;;
|
||||
|
||||
*)
|
||||
ocf_exit_reason \
|
||||
"Cannot start unhealthy PostgreSQL instance (monitor rc=${monitor_rc})"
|
||||
return "${monitor_rc}"
|
||||
;;
|
||||
esac
|
||||
|
||||
ocf_log info \
|
||||
"Ensuring container service ${SERVICE_NAME} is running"
|
||||
|
||||
if ! service_running; then
|
||||
if ! as_service_user \
|
||||
systemctl --user start "${SERVICE_NAME}"
|
||||
then
|
||||
ocf_exit_reason \
|
||||
"Failed to start ${SERVICE_NAME}"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
for i in $(seq 1 20); do
|
||||
container_running && break
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if ! container_running; then
|
||||
ocf_exit_reason \
|
||||
"Container ${CONTAINER_NAME} did not become running"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
|
||||
if ! pg_running; then
|
||||
#
|
||||
# OCF promotable-resource rule:
|
||||
# start() must produce an Unpromoted instance.
|
||||
#
|
||||
# A PostgreSQL data directory without standby.signal would start
|
||||
# writable/primary, so never start it from this action.
|
||||
#
|
||||
if ! pg_command '
|
||||
test -f "$PGDATA/standby.signal"
|
||||
'
|
||||
then
|
||||
ocf_exit_reason \
|
||||
"Refusing start: PGDATA is not configured as a standby (standby.signal missing)"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
|
||||
ocf_log info \
|
||||
"Starting PostgreSQL as an unpromoted standby inside ${CONTAINER_NAME}"
|
||||
|
||||
if ! pg_command '
|
||||
BINDIR="$(pg_config --bindir)" || exit 1
|
||||
exec "$BINDIR/pg_ctl" \
|
||||
-D "$PGDATA" \
|
||||
-l "$PGDATA/postgresql.log" \
|
||||
start -w
|
||||
'
|
||||
then
|
||||
ocf_exit_reason "pg_ctl start failed"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
managed_monitor
|
||||
monitor_rc=$?
|
||||
|
||||
case "${monitor_rc}" in
|
||||
"${OCF_SUCCESS}")
|
||||
ocf_log info \
|
||||
"PostgreSQL start completed in unpromoted state"
|
||||
return "${OCF_SUCCESS}"
|
||||
;;
|
||||
|
||||
"${OCF_RUNNING_PROMOTED}")
|
||||
ocf_exit_reason \
|
||||
"PostgreSQL became primary during start; stopping it to preserve OCF role semantics"
|
||||
|
||||
pg_command '
|
||||
BINDIR="$(pg_config --bindir)" || exit 1
|
||||
exec "$BINDIR/pg_ctl" \
|
||||
-D "$PGDATA" \
|
||||
stop -m fast -w
|
||||
' >/dev/null 2>&1 || true
|
||||
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
;;
|
||||
|
||||
*)
|
||||
return "${monitor_rc}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
managed_stop()
|
||||
{
|
||||
local i
|
||||
|
||||
if container_running && pg_running; then
|
||||
ocf_log info \
|
||||
"Stopping PostgreSQL cleanly inside ${CONTAINER_NAME}"
|
||||
|
||||
if ! pg_command '
|
||||
BINDIR="$(pg_config --bindir)" || exit 1
|
||||
exec "$BINDIR/pg_ctl" \
|
||||
-D "$PGDATA" \
|
||||
stop -m fast -w
|
||||
'
|
||||
then
|
||||
ocf_exit_reason "pg_ctl stop failed"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if service_running; then
|
||||
ocf_log info \
|
||||
"Stopping container service ${SERVICE_NAME}"
|
||||
|
||||
if ! as_service_user \
|
||||
systemctl --user stop "${SERVICE_NAME}"
|
||||
then
|
||||
ocf_exit_reason \
|
||||
"Failed to stop ${SERVICE_NAME}"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
for i in $(seq 1 20); do
|
||||
if ! service_running && ! container_running; then
|
||||
return "${OCF_SUCCESS}"
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if service_running; then
|
||||
ocf_exit_reason \
|
||||
"${SERVICE_NAME} remained active after stop"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
|
||||
if container_running; then
|
||||
ocf_exit_reason \
|
||||
"${CONTAINER_NAME} remained running after stop"
|
||||
return "${OCF_ERR_GENERIC}"
|
||||
fi
|
||||
|
||||
return "${OCF_SUCCESS}"
|
||||
}
|
||||
Reference in New Issue
Block a user