289 lines
7.7 KiB
Bash
Executable File
289 lines
7.7 KiB
Bash
Executable File
# Generic managed-containerd client helpers.
|
|
|
|
MC_PROXY_SOCKET_DEFAULT="/run/user/1000/managed-containerd/managed-containerd.sock"
|
|
|
|
MC_PROXY_MODULE_DIR="$(
|
|
cd "$(dirname "${BASH_SOURCE[0]}")" &&
|
|
pwd
|
|
)"
|
|
|
|
MC_PROXY_CLIENT_DEFAULT="${MC_PROXY_MODULE_DIR}/proxy_client.py"
|
|
|
|
|
|
# Query managed-containerd for the authoritative monitor result.
|
|
#
|
|
# stdout:
|
|
# <OCF rc>|<normalized diagnostic payload>
|
|
#
|
|
# return:
|
|
# 0 valid classified daemon result
|
|
# MC_PROXY_RC_TRANSPORT daemon transport/control-plane failure; bounded cache grace allowed
|
|
# MC_PROXY_RC_HARD_FAILURE configuration/protocol/result failure; fail closed, no cache grace
|
|
mc_proxy_monitor()
|
|
{
|
|
local socket_path="${MC_PROXY_SOCKET:-${MC_PROXY_SOCKET_DEFAULT}}"
|
|
local resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
local response rc
|
|
|
|
resource="${resource%%:*}"
|
|
|
|
if [ -z "${resource}" ]; then
|
|
ocf_log err \
|
|
"managed-containerd monitor requires OCF_RESOURCE_INSTANCE"
|
|
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
fi
|
|
|
|
if [ ! -S "${socket_path}" ]; then
|
|
ocf_log warn \
|
|
"managed-containerd socket is unavailable: ${socket_path}"
|
|
return "${MC_PROXY_RC_TRANSPORT}"
|
|
fi
|
|
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
ocf_log err \
|
|
"python3 is required for managed-containerd monitor transport"
|
|
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
fi
|
|
|
|
response="$(
|
|
timeout --kill-after=1s 3s \
|
|
python3 \
|
|
"${MC_PROXY_CLIENT:-${MC_PROXY_CLIENT_DEFAULT}}" \
|
|
"${socket_path}" \
|
|
"${resource}" \
|
|
"${MC_PROXY_RC_TRANSPORT}" \
|
|
"${MC_PROXY_RC_HARD_FAILURE}"
|
|
)"
|
|
rc=$?
|
|
|
|
case "${rc}" in
|
|
0)
|
|
;;
|
|
|
|
"${MC_PROXY_RC_TRANSPORT}"|124|137)
|
|
# MC_PROXY_RC_TRANSPORT = classified Python transport failure.
|
|
# 124/137 = timeout(1) expiry / forced termination.
|
|
ocf_log warn \
|
|
"managed-containerd authoritative monitor transport failure"
|
|
return "${MC_PROXY_RC_TRANSPORT}"
|
|
;;
|
|
|
|
"${MC_PROXY_RC_HARD_FAILURE}")
|
|
ocf_log err \
|
|
"managed-containerd authoritative monitor protocol or result failure"
|
|
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
;;
|
|
|
|
*)
|
|
ocf_log err \
|
|
"managed-containerd authoritative monitor client failure rc=${rc}"
|
|
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
;;
|
|
esac
|
|
|
|
if [ -z "${response}" ]; then
|
|
ocf_log err \
|
|
"managed-containerd authoritative monitor returned an empty result"
|
|
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
fi
|
|
|
|
rc="${response%%|*}"
|
|
|
|
# Cache only healthy daemon-authoritative role observations. An
|
|
# application-level failure automatically invalidates any previous cache.
|
|
mc_proxy_monitor_cache_write "${rc}" || {
|
|
ocf_log warn \
|
|
"Unable to update managed-containerd authoritative monitor cache"
|
|
}
|
|
|
|
printf '%s\n' "${response}"
|
|
return 0
|
|
}
|
|
|
|
|
|
# Compare the legacy application monitor against a daemon-authoritative rc.
|
|
# The legacy result is diagnostic only and can never change the returned
|
|
# Pacemaker monitor status.
|
|
mc_proxy_legacy_shadow_monitor()
|
|
{
|
|
local authoritative_rc="$1"
|
|
local legacy_rc="$2"
|
|
local resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
|
|
resource="${resource%%:*}"
|
|
|
|
if [ "${legacy_rc}" -eq "${authoritative_rc}" ]; then
|
|
ocf_log info \
|
|
"legacy application shadow monitor: resource=${resource} authoritative_rc=${authoritative_rc} legacy_rc=${legacy_rc} comparison=MATCH"
|
|
else
|
|
ocf_log err \
|
|
"legacy application shadow monitor MISMATCH: resource=${resource} authoritative_rc=${authoritative_rc} legacy_rc=${legacy_rc}"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# Generic short-lived cache of the last healthy daemon-authoritative OCF role.
|
|
# This is control-plane state only; it contains no application-specific data.
|
|
#
|
|
# Use HA_RSCTMP when available because it is the standard resource-agent
|
|
# runtime location and is preferable to inventing a persistent filesystem path.
|
|
MC_PROXY_STATE_DIR_DEFAULT="${HA_RSCTMP:-/run/resource-agents}/managed-container"
|
|
|
|
|
|
mc_proxy_state_file()
|
|
{
|
|
local resource safe_resource
|
|
resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
resource="${resource%%:*}"
|
|
|
|
[ -n "${resource}" ] || return 1
|
|
|
|
safe_resource="$(
|
|
printf '%s' "${resource}" |
|
|
tr -c 'A-Za-z0-9_.-' '_'
|
|
)"
|
|
|
|
printf '%s/%s.monitor\n' \
|
|
"${MC_PROXY_STATE_DIR:-${MC_PROXY_STATE_DIR_DEFAULT}}" \
|
|
"${safe_resource}"
|
|
}
|
|
|
|
|
|
mc_proxy_monitor_cache_clear()
|
|
{
|
|
local state_file
|
|
|
|
state_file="$(mc_proxy_state_file)" || return 0
|
|
rm -f "${state_file}" 2>/dev/null || true
|
|
}
|
|
|
|
|
|
mc_proxy_monitor_cache_write()
|
|
{
|
|
local rc="$1"
|
|
local state_file state_dir tmp now
|
|
|
|
# Only healthy authoritative role observations are eligible for
|
|
# temporary control-plane grace.
|
|
case "${rc}" in
|
|
"${OCF_SUCCESS}"|"${OCF_RUNNING_PROMOTED}")
|
|
;;
|
|
*)
|
|
mc_proxy_monitor_cache_clear
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
state_file="$(mc_proxy_state_file)" || return 1
|
|
state_dir="${state_file%/*}"
|
|
|
|
mkdir -p -m 700 "${state_dir}" || return 1
|
|
|
|
now="$(date +%s)"
|
|
tmp="${state_file}.$$"
|
|
|
|
umask 077
|
|
printf '%s|%s|0
|
|
' "${now}" "${rc}" > "${tmp}" || {
|
|
rm -f "${tmp}" 2>/dev/null || true
|
|
return 1
|
|
}
|
|
|
|
chmod 600 "${tmp}" 2>/dev/null || true
|
|
mv -f "${tmp}" "${state_file}"
|
|
}
|
|
|
|
|
|
# Print a cached healthy OCF rc when its observation is still inside the
|
|
# configured daemon control-plane failure grace.
|
|
mc_proxy_cached_monitor()
|
|
{
|
|
local state_file observed rc failure_started
|
|
local now failure_age observation_age grace max_observation_age
|
|
local tmp
|
|
|
|
grace="${OCF_RESKEY_proxy_failure_grace_seconds:-15}"
|
|
|
|
case "${grace}" in
|
|
""|*[!0-9]*)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
[ "${grace}" -gt 0 ] || return 1
|
|
|
|
state_file="$(mc_proxy_state_file)" || return 1
|
|
[ -f "${state_file}" ] || return 1
|
|
|
|
IFS='|' read -r observed rc failure_started < "${state_file}" || {
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
}
|
|
|
|
case "${observed}" in
|
|
""|*[!0-9]*)
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
case "${failure_started}" in
|
|
""|*[!0-9]*)
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
case "${rc}" in
|
|
"${OCF_SUCCESS}"|"${OCF_RUNNING_PROMOTED}")
|
|
;;
|
|
*)
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
now="$(date +%s)"
|
|
observation_age=$((now - observed))
|
|
|
|
# A cached role must come from a reasonably recent successful daemon
|
|
# observation. This protects against resurrecting stale runtime state.
|
|
max_observation_age=$((grace + 60))
|
|
|
|
if [ "${observation_age}" -lt 0 ] ||
|
|
[ "${observation_age}" -gt "${max_observation_age}" ]; then
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
fi
|
|
|
|
# First failed daemon observation starts the bounded grace period.
|
|
if [ "${failure_started}" -eq 0 ]; then
|
|
failure_started="${now}"
|
|
tmp="${state_file}.$$"
|
|
|
|
umask 077
|
|
printf '%s|%s|%s
|
|
' "${observed}" "${rc}" "${failure_started}" > "${tmp}" || {
|
|
rm -f "${tmp}" 2>/dev/null || true
|
|
return 1
|
|
}
|
|
|
|
chmod 600 "${tmp}" 2>/dev/null || true
|
|
mv -f "${tmp}" "${state_file}" || return 1
|
|
fi
|
|
|
|
failure_age=$((now - failure_started))
|
|
|
|
if [ "${failure_age}" -lt 0 ] ||
|
|
[ "${failure_age}" -ge "${grace}" ]; then
|
|
mc_proxy_monitor_cache_clear
|
|
return 1
|
|
fi
|
|
|
|
printf '%s
|
|
' "${rc}"
|
|
return 0
|
|
}
|