222 lines
6.9 KiB
Bash
Executable file
222 lines
6.9 KiB
Bash
Executable file
#!/bin/bash
|
||
# Health check for manual taler-merchant (container taler-hacktivism).
|
||
# Style: check_exchange-health.sh — [OK] / [FAIL] / [WARN], exit 1 on critical fail.
|
||
#
|
||
# Keys checks: for each enabled [merchant-exchange-*] with EXCHANGE_BASE_URL,
|
||
# GET …/keys and (if set) verify MASTER_KEY appears in the response.
|
||
|
||
CONF="${TALER_MERCHANT_CONFIG:-/etc/taler-merchant/taler-merchant.conf}"
|
||
OVERRIDES=/etc/taler-merchant/merchant-overrides.conf
|
||
SOCK="/var/run/taler-merchant/httpd/merchant-http.sock"
|
||
SS_BIN=$(command -v ss)
|
||
CURL_BIN=$(command -v curl)
|
||
|
||
green() { echo -e "\e[32m$1\e[0m"; }
|
||
red() { echo -e "\e[31m$1\e[0m"; }
|
||
yellow() { echo -e "\e[33m$1\e[0m"; }
|
||
|
||
fail=0
|
||
ok() { green "[OK] $1"; }
|
||
bad() { red "[FAIL] $1"; fail=1; }
|
||
warn() { yellow "[WARN] $1"; }
|
||
|
||
is_url() {
|
||
case "$1" in
|
||
http://*|https://*) return 0 ;;
|
||
*) return 1 ;;
|
||
esac
|
||
}
|
||
|
||
# GET /keys; try primary URL then optional host-local fallbacks for same exchange.
|
||
# Writes body to $1 (path). Returns 0 on HTTP success with non-empty body.
|
||
fetch_keys() {
|
||
local out="$1"
|
||
local primary="$2"
|
||
shift 2
|
||
local url
|
||
for url in "$primary" "$@"; do
|
||
[ -z "$url" ] && continue
|
||
if $CURL_BIN -skf -m 6 "$url" -o "$out" 2>/dev/null \
|
||
|| $CURL_BIN -sf -m 6 "$url" -o "$out" 2>/dev/null; then
|
||
if [ -s "$out" ] && grep -qE 'master_public_key|"currency"' "$out" 2>/dev/null; then
|
||
echo "$url"
|
||
return 0
|
||
fi
|
||
fi
|
||
done
|
||
return 1
|
||
}
|
||
|
||
echo "=== Taler Merchant Health Check ==="
|
||
|
||
# --- 1–4: local service ---
|
||
if [ -S "$SOCK" ]; then
|
||
ok "socket exists: $SOCK"
|
||
else
|
||
bad "socket does NOT exist: $SOCK"
|
||
fi
|
||
|
||
if [ -n "$SS_BIN" ] && $SS_BIN -xl 2>/dev/null | grep -q "$SOCK"; then
|
||
ok "Merchant-HTTPD listening on socket"
|
||
elif [ -n "$SS_BIN" ]; then
|
||
bad "no listener on socket"
|
||
else
|
||
warn "ss not available — skip socket listener check"
|
||
fi
|
||
|
||
if pgrep -f taler-merchant-httpd >/dev/null 2>&1; then
|
||
ok "process taler-merchant-httpd"
|
||
else
|
||
bad "process taler-merchant-httpd is NOT running"
|
||
fi
|
||
|
||
if pgrep -f "nginx: master" >/dev/null 2>&1; then
|
||
ok "nginx master process"
|
||
else
|
||
bad "nginx master process is NOT running"
|
||
fi
|
||
|
||
# --- 5: merchant /config ---
|
||
if [ -n "$CURL_BIN" ]; then
|
||
cfg_ok=0
|
||
if [ -S "$SOCK" ] && $CURL_BIN -sf -m 3 --unix-socket "$SOCK" "http://localhost/config" >/dev/null 2>&1; then
|
||
ok "merchant /config via unix socket"
|
||
cfg_ok=1
|
||
elif $CURL_BIN -skf -m 3 "https://127.0.0.1:9010/config" >/dev/null 2>&1; then
|
||
ok "merchant /config via https://127.0.0.1:9010/config"
|
||
cfg_ok=1
|
||
elif $CURL_BIN -sf -m 3 "http://127.0.0.1:9010/config" >/dev/null 2>&1; then
|
||
ok "merchant /config via http://127.0.0.1:9010/config"
|
||
cfg_ok=1
|
||
fi
|
||
[ "$cfg_ok" -eq 0 ] && bad "merchant /config unreachable (socket and :9010)"
|
||
else
|
||
warn "curl missing — skip /config"
|
||
fi
|
||
|
||
# --- 6: exchange /keys for configured exchanges ---
|
||
echo "--- configured exchanges (/keys) ---"
|
||
|
||
# Emit lines: SECTION|DISABLED|BASE|MASTER (from overrides + optional taler-config)
|
||
list_exchanges() {
|
||
# Prefer site overrides file (authoritative for this host)
|
||
if [ -f "$OVERRIDES" ]; then
|
||
awk '
|
||
BEGIN { sec=""; dis="NO"; base=""; master="" }
|
||
/^\[merchant-exchange-/ {
|
||
if (sec != "") printf "%s|%s|%s|%s\n", sec, dis, base, master
|
||
sec=$0; gsub(/[\[\] \t\r]/, "", sec)
|
||
dis="NO"; base=""; master=""
|
||
next
|
||
}
|
||
/^\[/ {
|
||
if (sec != "") printf "%s|%s|%s|%s\n", sec, dis, base, master
|
||
sec=""; next
|
||
}
|
||
sec=="" { next }
|
||
/^[ \t]*#/ { next }
|
||
{
|
||
line=$0
|
||
sub(/[ \t]*#.*$/, "", line)
|
||
if (match(line, /^[ \t]*DISABLED[ \t]*=[ \t]*/)) {
|
||
dis=substr(line, RSTART+RLENGTH); gsub(/^[ \t]+|[ \t]+$/, "", dis)
|
||
} else if (match(line, /^[ \t]*EXCHANGE_BASE_URL[ \t]*=[ \t]*/)) {
|
||
base=substr(line, RSTART+RLENGTH); gsub(/^[ \t]+|[ \t]+$/, "", base)
|
||
} else if (match(line, /^[ \t]*MASTER_KEY[ \t]*=[ \t]*/)) {
|
||
master=substr(line, RSTART+RLENGTH); gsub(/^[ \t]+|[ \t]+$/, "", master)
|
||
}
|
||
}
|
||
END { if (sec != "") printf "%s|%s|%s|%s\n", sec, dis, base, master }
|
||
' "$OVERRIDES"
|
||
fi
|
||
}
|
||
|
||
if [ -z "$CURL_BIN" ]; then
|
||
bad "curl missing — cannot check exchange /keys"
|
||
else
|
||
exch_count=0
|
||
while IFS='|' read -r sec dis base master; do
|
||
[ -z "$sec" ] && continue
|
||
case "${dis:-NO}" in
|
||
YES|yes|true|True|1)
|
||
warn "exchange $sec: DISABLED — skip /keys"
|
||
continue
|
||
;;
|
||
esac
|
||
if ! is_url "$base"; then
|
||
# package stubs without URL (e.g. kudos only DISABLED)
|
||
warn "exchange $sec: no EXCHANGE_BASE_URL — skip"
|
||
continue
|
||
fi
|
||
exch_count=$((exch_count + 1))
|
||
base_slash="${base%/}/"
|
||
primary="${base_slash}keys"
|
||
keys_tmp=$(mktemp 2>/dev/null || echo "/tmp/m-keys-$$-${exch_count}.json")
|
||
|
||
# Fallbacks when public name is not reachable from container:
|
||
# host loopback ports published by podman (exchange :9011).
|
||
got_url=
|
||
if got_url=$(fetch_keys "$keys_tmp" "$primary" \
|
||
"http://127.0.0.1:9011/keys" \
|
||
"http://host.containers.internal:9011/keys" \
|
||
"http://10.0.2.2:9011/keys"); then
|
||
ok "exchange $sec: /keys reachable ($got_url)"
|
||
if [ -n "$master" ]; then
|
||
if grep -qF "$master" "$keys_tmp" 2>/dev/null; then
|
||
ok "exchange $sec: MASTER_KEY matches /keys"
|
||
else
|
||
mpks=$(grep -oE '"master_public_key"[[:space:]]*:[[:space:]]*"[^"]+"' "$keys_tmp" 2>/dev/null | head -2)
|
||
bad "exchange $sec: MASTER_KEY does not match /keys (config MASTER_KEY=$master)"
|
||
[ -n "$mpks" ] && warn " seen in /keys: $mpks"
|
||
fi
|
||
else
|
||
warn "exchange $sec: no MASTER_KEY in config — skip key match"
|
||
fi
|
||
else
|
||
bad "exchange $sec: no /keys from $primary (and local :9011 fallbacks)"
|
||
fi
|
||
rm -f "$keys_tmp" 2>/dev/null || true
|
||
done <<EOF
|
||
$(list_exchanges)
|
||
EOF
|
||
[ "$exch_count" -eq 0 ] && warn "no enabled exchanges with EXCHANGE_BASE_URL"
|
||
fi
|
||
|
||
# --- 7: helpers — ensure (no systemd) then require ---
|
||
if [ "${SKIP_ENSURE:-0}" != "1" ]; then
|
||
if [ -x /usr/local/bin/ensure_merchant_helpers.sh ]; then
|
||
echo "--- ensure_merchant_helpers ---"
|
||
/usr/local/bin/ensure_merchant_helpers.sh || warn "ensure_merchant_helpers exited non-zero"
|
||
elif [ -x "$(dirname "$0")/ensure_merchant_helpers.sh" ]; then
|
||
echo "--- ensure_merchant_helpers ---"
|
||
"$(dirname "$0")/ensure_merchant_helpers.sh" || warn "ensure_merchant_helpers exited non-zero"
|
||
fi
|
||
fi
|
||
|
||
live_helper() {
|
||
local p="$1"
|
||
pgrep -f "(^|/)(${p})( |$)" >/dev/null 2>&1
|
||
}
|
||
|
||
# Settlement needs wirewatch + depositcheck; others needed for ops.
|
||
for p in \
|
||
taler-merchant-webhook \
|
||
taler-merchant-kyccheck \
|
||
taler-merchant-wirewatch \
|
||
taler-merchant-depositcheck \
|
||
taler-merchant-exchangekeyupdate \
|
||
taler-merchant-reconciliation
|
||
do
|
||
if live_helper "$p"; then
|
||
ok "process $p"
|
||
else
|
||
bad "process $p not running"
|
||
fi
|
||
done
|
||
|
||
if [ "$fail" -eq 0 ]; then
|
||
green "=== ALL CRITICAL CHECKS PASSED ==="
|
||
exit 0
|
||
fi
|
||
red "=== SOME CHECKS FAILED ==="
|
||
exit 1
|