release 1.7.6: monpages obligatory ERROR with stack inventory

GOA monpages checks the full suite catalog (bank/exchange/taler
/monitoring/ plus surface/aptdeploy/mattermost/mail) and on-disk
discovery; FP only its own mon hosts. Default MONPAGES_REQUIRE_PUBLIC=1.
Also fix with_timeout re-running failed phases via || fallback.
This commit is contained in:
Hernâni Marques 2026-07-19 02:05:00 +02:00
parent 909e6a65da
commit c5034c0bb5
No known key found for this signature in database
GPG key ID: CB5738652768F7E9
11 changed files with 188 additions and 49 deletions

View file

@ -4,15 +4,23 @@
# Outside-in: curl https://<fqdn>/<path>/ and require a real HTML monitoring page.
# Catches: merchant JSON code 21 (Caddy handle missing), WP 404, empty stubs, deploy skip.
#
# Env (same knobs as host-agent HTML layout):
# Policy (v1.7.6+):
# • monpages is obligatory → missing/wrong pages are ERROR (exit 1).
# • GOA / hacktivism: full inventory of suite monitoring sites (catalog + on-disk discovery).
# • FP: only that stacks own monitoring hosts/paths (never GOA URLs).
# • Override soft mode only with MONPAGES_REQUIRE_PUBLIC=0 (emergency / staging).
#
# Env:
# MON_HOSTS space-separated FQDNs (default from TALER_DOMAIN)
# HTML_URL_OK path for OK page (default /monitoring/)
# HTML_URL_ERR optional err path; checked if MONPAGES_CHECK_ERR=1
# MONITORING_PAGE_URLS optional full URL list (overrides host+path construction)
# HTML_URL_ERR err path; checked if present on disk or MONPAGES_CHECK_ERR=1
# MONITORING_PAGE_URLS optional full URL list (overrides inventory construction)
# MONPAGES_EXTRA_URLS additional absolute URLs (space-separated)
# MONPAGES_INVENTORY auto|full|job (default auto)
# auto/full = stack inventory; job = only MON_HOSTS+paths
# MONPAGES_REQUIRE_PUBLIC default 1 (ERROR). 0 = WARN only (escape hatch).
# MONPAGES_STAGING_BASE / HTML_OUT / DEPLOY_WWW_ROOT trees for discovery
# TIMEOUT curl timeout (from lib.sh)
# MONPAGES_STAGING_BASE / HTML_OUT optional staging root for diagnosis
# HTML_OK_DIR staging subdir (default monitoring)
#
set -euo pipefail
ROOT=$(cd "$(dirname "$0")" && pwd)
@ -20,10 +28,11 @@ ROOT=$(cd "$(dirname "$0")" && pwd)
source "$ROOT/lib.sh"
set_area monpages
section "monpages · public monitoring HTML via FQDN (outside-in)"
section "monpages · public monitoring HTML via FQDN (outside-in, obligatory)"
# MONPAGES_REQUIRE_PUBLIC=0 → missing public pages are WARN (staging-only stacks / FP until vhost)
# Obligatory: public pages missing → ERROR. Soft only if explicitly disabled.
: "${MONPAGES_REQUIRE_PUBLIC:=1}"
: "${MONPAGES_INVENTORY:=auto}"
# Default hosts from domain (mirror run-host-report.sh)
if [ -z "${MON_HOSTS:-}" ]; then
@ -46,31 +55,131 @@ fi
HTML_URL_OK="${HTML_URL_OK:-/monitoring/}"
HTML_URL_ERR="${HTML_URL_ERR:-/monitoring_err/}"
# ensure leading + trailing slash
case "$HTML_URL_OK" in
/*) ;;
*) HTML_URL_OK="/$HTML_URL_OK" ;;
esac
case "$HTML_URL_OK" in
*/) ;;
*) HTML_URL_OK="${HTML_URL_OK}/" ;;
esac
_norm_path() {
local p="$1"
case "$p" in
/*) ;;
*) p="/$p" ;;
esac
case "$p" in
*/) ;;
*) p="${p}/" ;;
esac
printf '%s' "$p"
}
HTML_URL_OK="$(_norm_path "$HTML_URL_OK")"
HTML_URL_ERR="$(_norm_path "$HTML_URL_ERR")"
# Stack family: goa | fp | other — FP never checks GOA and vice versa.
_monpages_family() {
case "${TALER_DOMAIN:-}" in
*lefrancpaysan*|*francpaysan*) printf 'fp' ;;
hacktivism.ch|koopa) printf 'goa' ;;
*)
# Infer from MON_HOSTS if domain ambiguous
case " ${MON_HOSTS:-} " in
*lefrancpaysan*) printf 'fp' ;;
*hacktivism*) printf 'goa' ;;
*) printf 'other' ;;
esac
;;
esac
}
_host_allowed() {
local h="$1" fam="$2"
case "$fam" in
goa)
case "$h" in *.hacktivism.ch) return 0 ;; *) return 1 ;; esac
;;
fp)
case "$h" in *lefrancpaysan*) return 0 ;; *) return 1 ;; esac
;;
*) return 0 ;;
esac
}
# Emit https://host/path/ for each …/host/path/index.html under root (max depth 3).
_discover_tree() {
local root="$1" fam="$2"
local f rel host path
[ -n "$root" ] && [ -d "$root" ] || return 0
# layout: $root/<fqdn>/<url-path>/index.html
while IFS= read -r -d '' f; do
rel="${f#"${root%/}"/}"
host="${rel%%/*}"
path="${rel#*/}"
path="${path%/index.html}"
[ -n "$host" ] && [ -n "$path" ] || continue
_host_allowed "$host" "$fam" || continue
printf 'https://%s/%s/\n' "$host" "$path"
done < <(find "${root%/}" -mindepth 3 -maxdepth 3 -type f -name index.html -print0 2>/dev/null)
}
# Suite catalog of OK pages that must exist for this family.
_catalog_urls() {
local fam="$1" h
case "$fam" in
goa)
for h in bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch; do
printf 'https://%s/monitoring/\n' "$h"
done
for h in \
taler-monitoring-surface \
taler-monitoring-aptdeploy \
taler-monitoring-mattermost \
taler-monitoring-mail
do
printf 'https://taler.hacktivism.ch/%s/\n' "$h"
done
;;
fp)
for h in $MON_HOSTS; do
[ -z "$h" ] && continue
_host_allowed "$h" fp || continue
printf 'https://%s/monitoring/\n' "$h"
done
;;
esac
}
# Job-local URLs (this host-agent runs path).
_job_urls() {
local h p
for h in $MON_HOSTS; do
[ -z "$h" ] && continue
printf 'https://%s%s\n' "$h" "$HTML_URL_OK"
if [ "${MONPAGES_CHECK_ERR:-0}" = "1" ]; then
printf 'https://%s%s\n' "$h" "$HTML_URL_ERR"
fi
done
}
build_urls() {
local h p
local fam inv
fam="$(_monpages_family)"
inv="${MONPAGES_INVENTORY:-auto}"
if [ -n "${MONITORING_PAGE_URLS:-}" ]; then
# shellcheck disable=SC2086
printf '%s\n' $MONITORING_PAGE_URLS
else
for h in $MON_HOSTS; do
[ -z "$h" ] && continue
printf 'https://%s%s\n' "$h" "$HTML_URL_OK"
if [ "${MONPAGES_CHECK_ERR:-0}" = "1" ]; then
p="$HTML_URL_ERR"
case "$p" in /*) ;; *) p="/$p" ;; esac
case "$p" in */) ;; *) p="${p}/" ;; esac
printf 'https://%s%s\n' "$h" "$p"
fi
done
case "$inv" in
job)
_job_urls
;;
full|auto|*)
# Always include this jobs paths (host-agent post-check).
_job_urls
# Stack inventory: GOA = all suite mon sites; FP = only FP mon hosts.
if [ "$fam" = "goa" ] || [ "$fam" = "fp" ]; then
_catalog_urls "$fam"
# On-disk discovery (err pages only exist after failed runs; extras welcome)
_discover_tree "${DEPLOY_WWW_ROOT:-/var/www/monitoring-sites}" "$fam"
_discover_tree "${MONPAGES_STAGING_BASE:-${HTML_OUT:-${HTML_BASE:-$HOME/monitoring-sites-staging}}}" "$fam"
fi
;;
esac
fi
if [ -n "${MONPAGES_EXTRA_URLS:-}" ]; then
# shellcheck disable=SC2086
@ -115,7 +224,6 @@ _is_bare_url() {
esac
}
_mon_fail_or_soft() {
local label="$1" detail="$2"
if [ "${MONPAGES_REQUIRE_PUBLIC:-1}" != "1" ]; then
@ -182,7 +290,7 @@ while IFS= read -r line; do
done
[ "$skip" = "1" ] && continue
URLS+=("$line")
done < <(build_urls)
done < <(build_urls | sort -u)
# Bare URLs without trailing slash: optional soft check (default on).
# Many reverse proxies only redirect /monitoring → /monitoring/; bare may hit the app (code 21).
@ -202,7 +310,6 @@ if [ "${MONPAGES_CHECK_BARE:-1}" = "1" ]; then
done
[ "$skip" = "1" ] && continue
URLS+=("$u")
# mark bare for soft handling via env list
MONPAGES_BARE_URLS="${MONPAGES_BARE_URLS:-} $u"
done
export MONPAGES_BARE_URLS
@ -213,6 +320,8 @@ if [ "${#URLS[@]}" -eq 0 ]; then
exit 1
fi
_fam="$(_monpages_family)"
echo " family=${_fam} inventory=${MONPAGES_INVENTORY} require_public=${MONPAGES_REQUIRE_PUBLIC}"
echo " checking ${#URLS[@]} URL(s) via FQDN..."
ec=0
for u in "${URLS[@]}"; do
@ -221,8 +330,14 @@ done
if [ "$ec" -ne 0 ]; then
echo " hint: public monitoring HTML not served (404 / merchant code 21)"
echo " publish staging HTML + configure reverse-proxy/Caddy handle for /monitoring*"
echo " publish staging HTML + configure reverse-proxy/Caddy handle for mon paths"
echo " (on koopa: sudo ~/koopa-caddy/apply-monitoring-live.sh)"
if [ "$_fam" = "fp" ]; then
echo " FP: only FP mon hosts are checked — wire Infomaniak vhost for /monitoring*"
fi
if [ "$_fam" = "goa" ]; then
echo " GOA: full inventory (bank/exchange/taler /monitoring/ + surface/aptdeploy/mattermost/mail)"
fi
# Staging vs public diagnosis (host-agent sets MONPAGES_STAGING_BASE / HTML_OUT)
_stg="${MONPAGES_STAGING_BASE:-${HTML_OUT:-${HTML_BASE:-$HOME/monitoring-sites-staging}}}"
_ok_dir="${HTML_OK_DIR:-monitoring}"