taler-monitoring/taler-monitoring.sh

593 lines
23 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# taler-monitoring — public URL / stack checks for a Taler domain
#
# ./taler-monitoring.sh # local GOA (urls + inside + e2e)
# ./taler-monitoring.sh -d taler.net urls # public demo, no SSH
# ./taler-monitoring.sh --domain taler-ops.ch # public ops, no SSH
# TALER_DOMAIN=demo.taler.net ./taler-monitoring.sh urls
#
# Tags: [OK] [INFO] [WARN] [ERROR] [BLOCKER]
# Exit 0 only if every selected phase exits 0.
set -euo pipefail
ROOT=$(cd "$(dirname "$0")" && pwd)
# --- suite identity (also: --ver / --version / -V) ---
SOURCE_REPO_WEB="${SOURCE_REPO_WEB:-https://git.hacktivism.ch/hernani/taler-monitoring}"
SOURCE_REPO_GIT="${SOURCE_REPO_GIT:-https://git.hacktivism.ch/hernani/taler-monitoring.git}"
print_version() {
local ver tag commit commit_full dirty branch
ver="unknown"
if [ -f "$ROOT/VERSION" ]; then
ver=$(tr -d "[:space:]" <"$ROOT/VERSION" | sed "s/^v//")
fi
tag=""
commit="unknown"
commit_full=""
branch=""
dirty=""
if git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1; then
tag=$(git -C "$ROOT" describe --tags --exact-match 2>/dev/null || true)
if [ -z "$tag" ]; then
tag=$(git -C "$ROOT" describe --tags --abbrev=0 2>/dev/null || true)
fi
commit=$(git -C "$ROOT" rev-parse --short=12 HEAD 2>/dev/null || echo unknown)
commit_full=$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || true)
branch=$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
if [ -n "$(git -C "$ROOT" status --porcelain 2>/dev/null)" ]; then
dirty=" (dirty)"
fi
fi
if [ -n "$tag" ] && [ "$tag" = "v$ver" ]; then
printf "taler-monitoring %s\n" "$tag"
else
printf "taler-monitoring v%s\n" "$ver"
fi
printf "version: %s\n" "$ver"
if [ -n "$tag" ]; then
if [ "$tag" = "v$ver" ]; then
printf "tag: %s\n" "$tag"
else
printf "git-tag: %s (tree; VERSION file is %s)\n" "$tag" "$ver"
fi
fi
printf "commit: %s%s\n" "$commit" "$dirty"
[ -n "$branch" ] && [ "$branch" != "HEAD" ] && printf "branch: %s\n" "$branch"
printf "repo: %s\n" "$SOURCE_REPO_WEB"
printf "clone: %s\n" "$SOURCE_REPO_GIT"
if [ -n "$commit_full" ]; then
printf "commit-url: %s/src/commit/%s\n" "$SOURCE_REPO_WEB" "$commit_full"
fi
if [ -n "$tag" ]; then
printf "tag-url: %s/src/tag/%s\n" "$SOURCE_REPO_WEB" "$tag"
fi
}
# Fast path: version before loading lib.sh / phases
case "${1:-}" in
--ver|--version|-V)
print_version
exit 0
;;
esac
# Line-buffered stdout/stderr so redirected logs (tee/host-agent) flush each line.
if [ "${_TALER_MON_STDBUF:-0}" != "1" ] && command -v stdbuf >/dev/null 2>&1; then
export _TALER_MON_STDBUF=1
exec stdbuf -oL -eL bash "$0" "$@"
fi
usage() {
cat <<'EOF'
taler-monitoring — bank / exchange / merchant checks
Usage:
./taler-monitoring.sh [options] [phases...]
Phases:
urls public HTTPS (no SSH) ← default with --domain
(includes merchant /webui/ SPA fingerprints: version.txt, overlay, assets)
inside container status via SSH (local stack only)
versions taler packages vs deb.taler.net trixie + repo availability
sanity public + optional server
server server-side only (SSH)
e2e withdraw + pay (small amounts; remote aborts on login/KYC)
ladder withdraw/pay amount ladder (GOA ceiling or stage TESTPAYSAN max_wire)
auth401 merchant Basic-auth / case matrix (HTTP 401 paths; may create throwaway instance)
aptdeploy koopa podman apt-src smoke: taler-merchant in
koopa-taler-deploy-test-apt-src-trixie{,-testing}
surface REMOTE-ONLY public inventory (NOT in default/all/full):
ecosystem hosts (taler.net, gnunet.org, taler-systems.com, mattermost, …)
or -d DOMAIN → that domains surface; port/protocol/TLS/CVE (OSV)
mattermost Mattermost chat health (default mattermost.taler.net; SPA + /api/v4/system/ping)
mail MX/SMTP/IMAP for Taler mail (firefly, pixel/TSA, catalogued domains)
monpages public monitoring HTML via FQDN (obligatory ERROR; GOA full inventory, FP only FP)
uses MON_HOSTS + HTML_URL_OK (same as host-agent); v1.3.1+
devtesting fake-franken CHF via rusty.taler-ops.ch (taler-devtesting)
geniban + fake-incoming; needs SSH Host DEVTESTING_SSH (key to devtesting@rusty)
franken|fake-franken aliases for devtesting
all urls + inside + versions + sanity + e2e (SSH phases only on koopa)
full all + server + ladder + auth401 (maximum; long-running, needs secrets)
NOTE: surface is never included in all/full — pass it explicitly
Options:
-d, --domain DOMAIN load profile from domains.conf (bank/exchange/merchant)
presets: koopa | hacktivism.ch | taler.net | taler-ops.ch
| my.taler-ops.ch | stage.taler-ops.ch
unknown domain → heuristic hosts (see README)
local (koopa) allows SSH; others public-only
--bank URL bank base (hostname or https://…) — overrides profile
--exchange URL exchange base — overrides profile
--merchant URL merchant-backend base — overrides profile
--currency CODE expected currency (GOA, KUDOS, CHF, …); empty = report only
--no-probe do not probe alternate hosts (unknown domains only)
--lang en|fr UI/console language (sticky bar + badges)
overrides auto (FrancPaysan→fr, else en)
--ver, --version, -V print suite version, git commit, and repo URLs
-h, --help
Add a stack: edit domains.conf (name + bank + exchange + merchant + currency).
Examples:
./taler-monitoring.sh -d taler.net
./taler-monitoring.sh -d taler-ops.ch urls
./taler-monitoring.sh -d my.taler-ops.ch urls
./taler-monitoring.sh -d taler-ops.ch --merchant https://my.taler-ops.ch urls
./taler-monitoring.sh -d demo.taler.net --currency KUDOS
./taler-monitoring.sh --exchange https://exchange.taler-ops.ch \
--merchant https://my.taler-ops.ch --bank https://bank.taler-ops.ch urls
Env (same meaning):
TALER_DOMAIN BANK_PUBLIC EXCHANGE_PUBLIC MERCHANT_PUBLIC EXPECT_CURRENCY
TALER_DOMAINS_CONF SKIP_SSH=1
NO_COLOR=1 / CLICOLOR=0 disable green/yellow/red tags (default: coloured)
SKIP_SSH=1 NO_COLOR=1
RUN_TIMEOUT=600 whole-run wall clock seconds (default 600; 0=unlimited)
long phases (ladder/full/e2e) need a higher value or 0
DISK_WARN_USED_PCT=85 disk free: WARN when used ≥ this %
DISK_ERR_USED_PCT=95 disk free: ERROR when used ≥ this % (or avail=0 / 100%)
PERF_WARN_MS PERF_FAIL_MS (urls latency; default 8000 / 20000)
QR_CHECK=0 skip QR form + qrencode/zbarimg (urls phase)
QR_ECC=M qrencode ECC level (default M)
KOOPA_SSH KOOPA_SSH_FALLBACKS (default koopa → koopa-external)
METRICS_LOAD=0 skip host/container RAM/CPU probes (e2e/ladder/inside)
EXPECT_WEBUI_VERSION=1.6.11 pin merchant /webui/version.txt (urls)
EXPECT_WEBUI_OVERLAY=selfbuild require substr in version-overlay.txt
WEBUI_OVERLAY_DENY='master-11340' fail if overlay matches regex
CHECK_WEBUI_SPA=0 skip SPA fingerprint block in urls
AUTH401_* see check_auth401.sh / secrets.env.example
AUTH401_CONTINUE=1 / CONTINUE_ON_ERROR=1
auth401: run all groups, collect every ERROR (no mid-run halt)
APT_DEPLOY_SKIP=1 skip aptdeploy phase
APT_DEPLOY_CONTAINERS="name:suite …" default trixie + trixie-testing deploy-test pods
SURFACE_CVE=0 disable CVE queries in surface phase
SURFACE_CVE_LEVEL=warn|error bare Server-header versions default warn
(Debian package versions default error)
SURFACE_CATALOG=path override surface-catalog.conf
TALER_MON_LANG=en|fr same as --lang (default en; auto fr for *lefrancpaysan*)
Full load (GOA / hacktivism):
./taler-monitoring.sh -d hacktivism.ch full
# or explicit:
./taler-monitoring.sh -d hacktivism.ch urls inside versions sanity server e2e ladder auth401
# hacktivism host-agent also runs aptdeploy (apt-src merchant containers on koopa)
Remote surface / ecosystem (explicit only — never default):
./taler-monitoring.sh surface
./taler-monitoring.sh -d hacktivism.ch surface
./taler-monitoring.sh -d lefrancpaysan.ch surface
./taler-monitoring.sh mattermost # mattermost.taler.net chat health
./taler-monitoring.sh mail # firefly + pixel MX/SMTP/IMAP
SPA pin after selfbuild:
EXPECT_WEBUI_VERSION=1.6.11 EXPECT_WEBUI_OVERLAY=selfbuild-v1.6.11 \\
WEBUI_OVERLAY_DENY='c778af04|master-11340' \\
./taler-monitoring.sh -d hacktivism.ch urls
EOF
}
# shellcheck source=lib.sh
source "$ROOT/lib.sh"
# ensure i18n after domain may change later
i18n_init 2>/dev/null || true
# Optional gitignored secrets.env next to this suite (or MONITORING_SECRETS_ENV=)
load_monitoring_secrets_env 2>/dev/null || true
# Persist #NNN + progress across check_*.sh child processes
TALER_MON_STATE=$(mktemp "${TMPDIR:-/tmp}/taler-mon-state.XXXXXX")
export TALER_MON_STATE
trap 'rm -f "${TALER_MON_STATE:-}"' EXIT
PHASES=()
DOMAIN_SET=0
BANK_OVERRIDE=""
EXCHANGE_OVERRIDE=""
MERCHANT_OVERRIDE=""
CURRENCY_OVERRIDE=""
NO_PROBE=0
while [ $# -gt 0 ]; do
case "$1" in
--lang)
[ $# -ge 2 ] || { echo "error: --lang needs en|fr" >&2; exit 2; }
_lang="$2"
case "$_lang" in
fr|FR|fra|french) TALER_MON_LANG=fr ;;
en|EN|eng|english) TALER_MON_LANG=en ;;
*) echo "error: unknown language '$_lang' (use en or fr)" >&2; exit 2 ;;
esac
TALER_MON_LANG_SET=1
export TALER_MON_LANG TALER_MON_LANG_SET
shift 2
i18n_init 2>/dev/null || true
;;
-h|--help) usage; exit 0 ;;
--ver|--version|-V) print_version; exit 0 ;;
-d|--domain)
[ $# -ge 2 ] || { echo "missing arg for $1" >&2; exit 2; }
apply_taler_domain "$2"
DOMAIN_SET=1
TALER_DOMAIN_FROM_CLI=1
shift 2
;;
--bank)
[ $# -ge 2 ] || { echo "missing arg for $1" >&2; exit 2; }
BANK_OVERRIDE="${2%/}"; shift 2
;;
--exchange)
[ $# -ge 2 ] || { echo "missing arg for $1" >&2; exit 2; }
EXCHANGE_OVERRIDE="${2%/}"; shift 2
;;
--merchant)
[ $# -ge 2 ] || { echo "missing arg for $1" >&2; exit 2; }
MERCHANT_OVERRIDE="${2%/}"; shift 2
;;
--currency)
[ $# -ge 2 ] || { echo "missing arg for $1" >&2; exit 2; }
CURRENCY_OVERRIDE="$2"; shift 2
;;
--no-probe) NO_PROBE=1; shift ;;
urls|inside|versions|sanity|server|e2e|ladder|goa-ladder|auth401|aptdeploy|apt-deploy|apt_src|surface|ecosystem|mattermost|mail|monpages|pages|devtesting|franken|fake-franken|fake_franken|all|full) PHASES+=("$1"); shift ;;
*)
# bare domain shorthand: ./taler-monitoring.sh taler.net
if [[ "$1" == *.* && "$1" != *://* && "$1" != -* ]]; then
apply_taler_domain "$1"
DOMAIN_SET=1
TALER_DOMAIN_FROM_CLI=1
shift
else
echo "Unknown: $1" >&2; usage >&2; exit 2
fi
;;
esac
done
if [ "$NO_PROBE" = "1" ]; then
TALER_DOMAIN_PROBE=0
fi
if [ -n "$CURRENCY_OVERRIDE" ]; then
EXPECT_CURRENCY="$CURRENCY_OVERRIDE"
fi
if [ -n "$BANK_OVERRIDE" ]; then BANK_PUBLIC="$BANK_OVERRIDE"; fi
if [ -n "$EXCHANGE_OVERRIDE" ]; then EXCHANGE_PUBLIC="$EXCHANGE_OVERRIDE"; fi
if [ -n "$MERCHANT_OVERRIDE" ]; then MERCHANT_PUBLIC="$MERCHANT_OVERRIDE"; fi
# Only koopa may use SSH. Remote domains: public + optional e2e (no SSH).
if [ "${LOCAL_STACK:-1}" != "1" ]; then
SKIP_SSH=1
fi
# Remote: no SSH; ATM withdraw ladder set in check_e2e (smaller notes)
if [ "${LOCAL_STACK}" != "1" ]; then
E2E_FAKE_INCOMING=0
E2E_REMOTE=1
: "${E2E_WITHDRAW_VALUES:=10 20 50}"
: "${E2E_PAY_VALUES:=0.01 0.05 0.1 1}"
fi
# Export so check_*.sh (re-source lib) see the same targets via env
export TALER_DOMAIN BANK_PUBLIC EXCHANGE_PUBLIC MERCHANT_PUBLIC
export EXPECT_CURRENCY SKIP_SSH LOCAL_STACK TALER_DOMAIN_PROBE CHECK_LANDING
export TALER_DOMAIN_FROM_CLI="${TALER_DOMAIN_FROM_CLI:-0}"
export DOMAIN_SET="${DOMAIN_SET:-0}"
export WITHDRAW_AMT PAY_AMT CREDIT_AMT MERCHANT_INSTANCE
export E2E_FAKE_INCOMING E2E_REMOTE E2E_VARIABLE E2E_ATM_MAX
export E2E_WITHDRAW_VALUES E2E_PAY_VALUES E2E_USE_TEMPLATES E2E_TEMPLATE_MAP
export PAIVANA_PUBLIC E2E_PAIVANA E2E_PAIVANA_TEMPLATE E2E_PAIVANA_AMOUNT E2E_PAIVANA_INSTANCE
export INSIDE_PROFILE INSIDE_SSH
export INSIDE_BANK_CTR INSIDE_EXCHANGE_CTR INSIDE_MERCHANT_CTR
export INSIDE_BANK_PORT INSIDE_EXCHANGE_PORT INSIDE_MERCHANT_PORT
export INSIDE_DNS_BANK INSIDE_DNS_EXCHANGE INSIDE_DNS_MERCHANT
# Ladder: withdraw then pay — 0 + random mids + max-1 + max (see check_goa_ladder.sh).
# Defaults so set -u export is safe when vars were never set by caller.
: "${LADDER_STEPS:=23}"
: "${LADDER_MIN_AMOUNT:=0.000001}"
: "${LADDER_CONFIRM_POLLS:=40}"
: "${LADDER_MAX_RUNGS:=99}"
: "${LADDER_TIMEOUT_S:=3600}"
: "${LADDER_REPORT_DIR:=}"
: "${LADDER_SETTLE_ROUNDS:=18}"
: "${LADDER_SETTLE_SLEEP:=2}"
: "${LADDER_MAX_AMOUNT:=4503599627370496}"
: "${LADDER_INCLUDE_ZERO:=1}"
: "${LADDER_INCLUDE_MAX:=1}"
: "${LADDER_HIGH_FROM:=1000000}"
: "${LADDER_HIGH_RUNGS:=12}"
: "${LADDER_LOAD:=1}"
: "${LADDER_PAY:=1}"
: "${LADDER_WITHDRAW_SCALE:=1.5}"
: "${LADDER_PAY_SETTLE_ROUNDS:=6}"
export LADDER_STEPS LADDER_MIN_AMOUNT LADDER_CONFIRM_POLLS LADDER_MAX_RUNGS LADDER_TIMEOUT_S LADDER_REPORT_DIR
export LADDER_SETTLE_ROUNDS LADDER_SETTLE_SLEEP EXP_PW_FILE EXP_USER
export LADDER_MAX_AMOUNT LADDER_INCLUDE_ZERO LADDER_INCLUDE_MAX
export LADDER_HIGH_FROM LADDER_HIGH_RUNGS LADDER_LOAD
export LADDER_PAY LADDER_WITHDRAW_SCALE LADDER_PAY_SETTLE_ROUNDS
export TALER_DOMAIN_APPLIED=1
i18n_init 2>/dev/null || true
# Optional: auth401 and other phases may honor CONTINUE_ON_ERROR
export CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-}"
export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR:-}}"
# Default phases
# - local GOA (koopa): urls + inside + versions + e2e
# - FrancPaysan STAGE: urls + inside (stagepaysan) + versions + e2e
# - other remote: urls only (pass e2e/inside explicitly)
if [ "${#PHASES[@]}" -eq 0 ]; then
if [ "${LOCAL_STACK}" = "1" ]; then
PHASES=(urls inside versions e2e)
elif [ "${EXPECT_CURRENCY:-}" = "TESTPAYSAN" ] \
|| [[ "${TALER_DOMAIN:-}" == stage.*lefrancpaysan* ]] \
|| [[ "${TALER_DOMAIN:-}" == *stage.lefrancpaysan* ]]; then
# Like hacktivism breadth: public urls + inside (stagepaysan) + versions + e2e
PHASES=(urls inside versions e2e)
else
PHASES=(urls)
fi
fi
OUT_PHASES=()
for p in "${PHASES[@]}"; do
if [ "$p" = "all" ]; then
if [ "${LOCAL_STACK}" = "1" ]; then
OUT_PHASES+=(urls inside versions sanity e2e)
elif [ -n "${INSIDE_SSH:-}" ] || [ "${INSIDE_PROFILE:-}" = "stage-lfp" ]; then
OUT_PHASES+=(urls inside versions e2e)
else
# remote without inside SSH: public + optional e2e
OUT_PHASES+=(urls versions e2e)
fi
elif [ "$p" = "full" ]; then
# Maximum coverage. Long. Needs secrets for e2e/ladder/auth401.
if [ "${LOCAL_STACK}" = "1" ]; then
OUT_PHASES+=(urls inside versions sanity server e2e ladder auth401)
elif [ -n "${INSIDE_SSH:-}" ] || [ "${INSIDE_PROFILE:-}" = "stage-lfp" ]; then
OUT_PHASES+=(urls inside versions e2e ladder auth401)
else
OUT_PHASES+=(urls versions e2e auth401)
fi
else
OUT_PHASES+=("$p")
fi
done
PHASES=()
seen=" "
for p in "${OUT_PHASES[@]}"; do
# server = koopa-only. inside allowed on koopa OR stagepaysan (INSIDE_SSH).
case "$p" in
server)
if [ "${LOCAL_STACK}" != "1" ]; then
echo "[INFO] skip phase 'server' (koopa only)" >&2
continue
fi
;;
inside)
if [ "${LOCAL_STACK}" != "1" ] && [ -z "${INSIDE_SSH:-}" ] && [ "${INSIDE_PROFILE:-}" != "stage-lfp" ]; then
echo "[INFO] skip phase 'inside' (no INSIDE_SSH / not local)" >&2
continue
fi
;;
esac
case "$seen" in *" $p "*) ;; *) PHASES+=("$p"); seen="$seen$p " ;; esac
done
if [ "${#PHASES[@]}" -eq 0 ]; then
PHASES=(urls)
fi
# Global expected check count for progress bar done/total (override: PROGRESS_TOTAL=N).
# One estimate for the whole run — phase summary() must NOT reset this mid-run.
# Prefer slightly high; lib.sh only grows if short (never shrinks until progress_finish).
if [ "${PROGRESS_TOTAL:-0}" = "0" ] || [ -z "${PROGRESS_TOTAL:-}" ]; then
_pt=0
for p in "${PHASES[@]}"; do
case "$p" in
urls) _pt=$((_pt + 95)) ;; # +qr + webui SPA fingerprints
inside) _pt=$((_pt + 30)) ;;
versions) _pt=$((_pt + 35)) ;; # + disk probes can be many
sanity) _pt=$((_pt + 35)) ;;
server) _pt=$((_pt + 20)) ;;
# e2e: ATM ladder emits many INFO lines (coins before/after each note)
e2e) _pt=$((_pt + 240)) ;;
ladder|goa-ladder) _pt=$((_pt + 120)) ;;
auth401) _pt=$((_pt + 70)) ;;
aptdeploy) _pt=$((_pt + 40)) ;;
surface|ecosystem) _pt=$((_pt + 120)) ;;
monpages|pages) _pt=$((_pt + 20)) ;;
mattermost) _pt=$((_pt + 25)) ;;
mail) _pt=$((_pt + 40)) ;;
devtesting|franken|fake-franken|fake_franken) _pt=$((_pt + 12)) ;;
esac
done
# monpages post-check after htmlify can add another inventory pass
case " ${PHASES[*]} " in
*" monpages "*|*" pages "*) _pt=$((_pt + 12)) ;;
esac
set_progress_total "$_pt"
unset _pt
fi
# Whole-run wall clock (default 10 min). Override: RUN_TIMEOUT=0 (unlimited).
: "${RUN_TIMEOUT:=600}"
export RUN_TIMEOUT
MON_T0=$(date +%s)
export MON_T0
RUN_TIMED_OUT=0
RUN_TIMEOUT_AT_PHASE=""
RUN_SKIPPED_PHASES=()
mon_seconds_left() {
if [ "${RUN_TIMEOUT:-0}" -eq 0 ]; then
printf '%s' "999999"
return 0
fi
local now left
now=$(date +%s)
left=$((RUN_TIMEOUT - (now - MON_T0)))
[ "$left" -lt 0 ] && left=0
printf '%s' "$left"
}
# Run one phase script under remaining RUN_TIMEOUT budget.
# Exit 124 (timeout utility) → mark RUN_TIMED_OUT.
run_phase() {
local phase="$1" script="$2" left rc
left=$(mon_seconds_left)
if [ "${RUN_TIMEOUT:-0}" -gt 0 ] && [ "$left" -le 0 ]; then
RUN_TIMED_OUT=1
RUN_TIMEOUT_AT_PHASE="${RUN_TIMEOUT_AT_PHASE:-$phase}"
return 1
fi
if [ "${RUN_TIMEOUT:-0}" -eq 0 ]; then
"$script"
return $?
fi
# Keep at least 2s so timeout(1) can start the child.
[ "$left" -lt 2 ] && left=2
set +e
with_timeout "$left" "$script"
rc=$?
set -e
if [ "$rc" -eq 124 ]; then
RUN_TIMED_OUT=1
RUN_TIMEOUT_AT_PHASE="$phase"
return 1
fi
return "$rc"
}
# How inside/versions will reach containers (host-podman vs ssh)
_INSIDE_ACCESS_HINT=ssh
if [ "${INSIDE_PODMAN:-0}" = "1" ] || [ "${INSIDE_MODE:-}" = "local-podman" ]; then
_INSIDE_ACCESS_HINT=host-podman
elif [ "${SKIP_SSH:-0}" = "1" ]; then
_INSIDE_ACCESS_HINT=skipped
elif [ "${LOCAL_STACK:-0}" = "1" ] && command -v podman >/dev/null 2>&1 \
&& podman ps --format '{{.Names}}' 2>/dev/null | grep -qE 'taler-hacktivism'; then
_INSIDE_ACCESS_HINT=host-podman
fi
printf 'target domain=%s\n' "${TALER_DOMAIN}"
printf ' bank %s\n' "$BANK_PUBLIC"
printf ' exchange %s\n' "$EXCHANGE_PUBLIC"
printf ' merchant %s\n' "$MERCHANT_PUBLIC"
printf ' currency expect=%s\n' "${EXPECT_CURRENCY:-any}"
printf ' phases %s\n' "${PHASES[*]}"
printf ' flags LOCAL_STACK=%s SKIP_SSH=%s INSIDE_PODMAN=%s INSIDE_MODE=%s\n' \
"${LOCAL_STACK:-}" "${SKIP_SSH:-0}" "${INSIDE_PODMAN:-0}" "${INSIDE_MODE:-}"
printf ' flags KOOPA_SSH=%s INSIDE_SSH=%s INSIDE_PROFILE=%s\n' \
"${KOOPA_SSH:-}" "${INSIDE_SSH:-}" "${INSIDE_PROFILE:-}"
printf ' access inside/versions → %s' "$_INSIDE_ACCESS_HINT"
case "$_INSIDE_ACCESS_HINT" in
host-podman) printf ' (podman exec on this host; IDs inside.host-*)\n' ;;
ssh) printf ' (SSH then podman; IDs inside.ssh-*)\n' ;;
skipped) printf ' (SKIP_SSH=1)\n' ;;
*) printf '\n' ;;
esac
if [ "${RUN_TIMEOUT:-0}" -eq 0 ]; then
printf ' run_timeout unlimited (RUN_TIMEOUT=0)\n'
else
printf ' run_timeout %ss wall clock (RUN_TIMEOUT=; 0=unlimited)\n' "$RUN_TIMEOUT"
fi
printf ' progress %s/%s (global done/total · set PROGRESS_TOTAL= to override; PROGRESS_OFF=1 to hide)\n' \
"${PROGRESS_DONE:-0}" "${PROGRESS_TOTAL:-0}"
unset _INSIDE_ACCESS_HINT
chmod +x "$ROOT"/check_*.sh 2>/dev/null || true
ec=0
for p in "${PHASES[@]}"; do
if [ "$RUN_TIMED_OUT" = "1" ]; then
RUN_SKIPPED_PHASES+=("$p")
continue
fi
left=$(mon_seconds_left)
if [ "${RUN_TIMEOUT:-0}" -gt 0 ] && [ "$left" -le 0 ]; then
RUN_TIMED_OUT=1
RUN_TIMEOUT_AT_PHASE="${RUN_TIMEOUT_AT_PHASE:-$p}"
RUN_SKIPPED_PHASES+=("$p")
continue
fi
case "$p" in
urls) run_phase urls "$ROOT/check_urls.sh" || ec=1 ;;
inside) run_phase inside "$ROOT/check_inside.sh" || ec=1 ;;
versions) run_phase versions "$ROOT/check_versions.sh" || ec=1 ;;
sanity) run_phase sanity "$ROOT/check_sanity.sh" || ec=1 ;;
server) run_phase server "$ROOT/check_server.sh" || ec=1 ;;
e2e) run_phase e2e "$ROOT/check_e2e.sh" || ec=1 ;;
ladder|goa-ladder) run_phase ladder "$ROOT/check_goa_ladder.sh" || ec=1 ;;
auth401) run_phase auth401 "$ROOT/check_auth401.sh" || ec=1 ;;
aptdeploy|apt-deploy|apt_src) run_phase aptdeploy "$ROOT/check_apt_deploy.sh" || ec=1 ;;
surface|ecosystem) run_phase surface "$ROOT/check_surface.sh" || ec=1 ;;
mattermost) run_phase mattermost "$ROOT/check_mattermost.sh" || ec=1 ;;
mail) run_phase mail "$ROOT/check_mail.sh" || ec=1 ;;
monpages|pages) run_phase monpages "$ROOT/check_monitoring_pages.sh" || ec=1 ;;
devtesting|franken|fake-franken|fake_franken)
run_phase devtesting "$ROOT/check_devtesting.sh" || ec=1
;;
esac
done
# Extraordinary run-budget failure: always report at end; HTML links top → here.
if [ "$RUN_TIMED_OUT" = "1" ]; then
ec=1
elapsed=$(( $(date +%s) - MON_T0 ))
skipped="${RUN_SKIPPED_PHASES[*]:-}"
# Stable id for jump links (console HTML + err-top banner)
printf '\n'
printf '╔══════════════════════════════════════════════════════════╗\n'
printf '║ RUN TIMEOUT · extraordinary (see jump target below) ║\n'
printf '╚══════════════════════════════════════════════════════════╝\n'
printf '┌ ERROR ┐ #run.timeout-01 RUN_TIMEOUT exceeded · budget %ss · elapsed %ss\n' \
"$RUN_TIMEOUT" "$elapsed"
printf ' detail: wall-clock limit hit'
if [ -n "${RUN_TIMEOUT_AT_PHASE:-}" ]; then
printf ' during/after phase "%s"' "$RUN_TIMEOUT_AT_PHASE"
fi
if [ -n "$skipped" ]; then
printf ' · skipped: %s' "$skipped"
fi
printf '\n'
printf ' id=run.timeout-01\n'
printf ' hint: raise RUN_TIMEOUT= (seconds) or set RUN_TIMEOUT=0 for unlimited\n'
printf -- '┌ RUN TIMEOUT · extraordinary ┐\n'
printf -- ' • run.timeout-01 [run] RUN_TIMEOUT=%ss exceeded (elapsed %ss)%s%s\n' \
"$RUN_TIMEOUT" "$elapsed" \
"${RUN_TIMEOUT_AT_PHASE:+ · phase $RUN_TIMEOUT_AT_PHASE}" \
"${skipped:+ · skipped $skipped}"
printf -- '--- ERRORS (failed checks) ---\n'
printf -- ' • run.timeout-01 [run] RUN_TIMEOUT=%ss exceeded (elapsed %ss)\n' \
"$RUN_TIMEOUT" "$elapsed"
fi
# Snap progress bar to real global count (done/total) once for the whole run.
# Reload TALER_MON_STATE — check_*.sh update done/total in a separate process.
if type _mon_state_load >/dev/null 2>&1; then
_mon_state_load
fi
if type progress_finish >/dev/null 2>&1; then
printf '\n'
progress_finish
fi
exit "$ec"