landing-stats: make collector multi-stack reusable
This commit is contained in:
parent
e1cc6d7141
commit
b6c3225e97
7 changed files with 291 additions and 58 deletions
|
|
@ -1,10 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
# Central landing-stats collector for hernani@koopa (user systemd timer).
|
||||
# Generic landing-stats orchestrator (any Taler stack).
|
||||
#
|
||||
# - Bank: full account+ledger scan via collect_bank_stats.py → bank container
|
||||
# - Exchange / merchant: existing in-container scripts, then amount_alt enrich
|
||||
# - All three: container RSS / loadavg / top procs via mem-snapshot (podman exec)
|
||||
# - Never wipes a good stats.json on failure (writes stats-run.json only)
|
||||
# Defaults = GOA / hacktivism on koopa. Override via env or EnvironmentFile
|
||||
# (see profiles: goa | stage-testpaysan).
|
||||
#
|
||||
# - Bank: collect_bank_stats.py (currency-agnostic) → stats.json
|
||||
# - Optional: exchange / merchant in-container generators
|
||||
# - Optional: container RSS/loadavg merge
|
||||
# - Publish: podman into container landings and/or host dirs (shared bank stats)
|
||||
# - Never wipes a good stats.json on failure (stats-run.json only)
|
||||
#
|
||||
# Install: scripts/taler-landing/install-landing-stats-host.sh
|
||||
set -euo pipefail
|
||||
|
|
@ -12,6 +16,20 @@ set -euo pipefail
|
|||
export TZ="${TZ:-Europe/Zurich}"
|
||||
export PATH="${HOME}/.local/bin:/usr/local/bin:/usr/bin:/bin${PATH:+:$PATH}"
|
||||
|
||||
# Optional stack profile: ~/.config/taler-landing/stack.env or STACK_ENV_FILE
|
||||
if [ -n "${STACK_ENV_FILE:-}" ] && [ -f "${STACK_ENV_FILE}" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
. "${STACK_ENV_FILE}"
|
||||
set +a
|
||||
elif [ -f "${HOME}/.config/taler-landing/stack.env" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
. "${HOME}/.config/taler-landing/stack.env"
|
||||
set +a
|
||||
fi
|
||||
|
||||
log() { printf '%s %s\n' "$(date -Iseconds)" "$*"; }
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
|
@ -22,6 +40,8 @@ elif [ -f "${HOME}/.local/lib/taler-landing/collect_bank_stats.py" ]; then
|
|||
LIB="${HOME}/.local/lib/taler-landing"
|
||||
elif [ -f "${HOME}/src/koopa/koopa-admin-log/scripts/taler-landing/collect_bank_stats.py" ]; then
|
||||
LIB="${HOME}/src/koopa/koopa-admin-log/scripts/taler-landing"
|
||||
elif [ -f "${HOME}/taler/src/koopa-admin-log/scripts/taler-landing/collect_bank_stats.py" ]; then
|
||||
LIB="${HOME}/taler/src/koopa-admin-log/scripts/taler-landing"
|
||||
else
|
||||
LIB="$ROOT"
|
||||
fi
|
||||
|
|
@ -29,6 +49,13 @@ fi
|
|||
ADMIN_LOG="${ADMIN_LOG:-${HOME}/src/koopa/koopa-admin-log}"
|
||||
SECRETS_BANK="${SECRETS_BANK:-${HOME}/src/koopa/koopa-admin-secrets/koopa/host-root/taler-bank}"
|
||||
|
||||
# Which phases to run (1/0)
|
||||
COLLECT_BANK="${COLLECT_BANK:-1}"
|
||||
COLLECT_EXCHANGE="${COLLECT_EXCHANGE:-1}"
|
||||
COLLECT_MERCHANT="${COLLECT_MERCHANT:-1}"
|
||||
COLLECT_RESOURCES="${COLLECT_RESOURCES:-1}"
|
||||
|
||||
# Containers (empty = skip podman publish for that role)
|
||||
BANK_CTR="${BANK_CTR:-taler-hacktivism-bank}"
|
||||
EX_CTR="${EX_CTR:-taler-hacktivism-exchange-ansible}"
|
||||
MER_CTR="${MER_CTR:-taler-hacktivism}"
|
||||
|
|
@ -36,10 +63,19 @@ MER_CTR="${MER_CTR:-taler-hacktivism}"
|
|||
BANK_URL="${BANK_URL:-http://127.0.0.1:9012}"
|
||||
BANK_PUBLIC_URL="${BANK_PUBLIC_URL:-https://bank.hacktivism.ch}"
|
||||
EXCHANGE_CONFIG_URL="${EXCHANGE_CONFIG_URL:-https://exchange.hacktivism.ch/config}"
|
||||
BANK_CURRENCY="${BANK_CURRENCY:-}" # empty → auto from bank/exchange /config
|
||||
STATS_SOURCE_LABEL="${STATS_SOURCE_LABEL:-host collect_bank_stats.py}"
|
||||
|
||||
# In-container landing roots (podman publish)
|
||||
BANK_LANDING_IN="${BANK_LANDING_IN:-/var/www/bank-landing}"
|
||||
EX_LANDING_IN="${EX_LANDING_IN:-/var/www/exchange-landing}"
|
||||
MER_LANDING_IN="${MER_LANDING_IN:-/var/www/merchant-landing}"
|
||||
PUBLISH_PODMAN="${PUBLISH_PODMAN:-1}"
|
||||
|
||||
# Host dirs that should get the *same* bank stats.json (shared public feed).
|
||||
# Colon-separated (systemd EnvironmentFile-safe). Example stage:
|
||||
# HOST_STATS_DIRS=/var/www/lfp-stage/bank:/var/www/lfp-stage/exchange:/var/www/lfp-stage/merchant
|
||||
HOST_STATS_DIRS="${HOST_STATS_DIRS:-}"
|
||||
|
||||
WORKDIR="${LANDING_STATS_WORKDIR:-${XDG_RUNTIME_DIR:-/tmp}/taler-landing-stats}"
|
||||
mkdir -p "$WORKDIR"
|
||||
|
|
@ -64,7 +100,9 @@ MEM_SRC="${MEM_SNAPSHOT_SRC:-$ADMIN_LOG/scripts/taler-shared/mem-snapshot.sh}"
|
|||
|
||||
merge_container_resources() {
|
||||
local label="$1" ctr="$2" stats_file="$3"
|
||||
[ "${COLLECT_RESOURCES}" = "1" ] || return 0
|
||||
[ -f "$stats_file" ] || return 0
|
||||
[ -n "$ctr" ] || return 0
|
||||
if ! ctr_running "$ctr"; then
|
||||
log "WARN: $label resources: $ctr not running"
|
||||
return 1
|
||||
|
|
@ -95,6 +133,36 @@ merge_container_resources() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Write stats.json into host directories (shared feed: same file everywhere).
|
||||
publish_host_stats() {
|
||||
local src_stats="$1" src_run="${2:-}"
|
||||
[ -n "${HOST_STATS_DIRS:-}" ] || return 0
|
||||
[ -f "$src_stats" ] || return 0
|
||||
local d paths
|
||||
# Accept colon or space separators
|
||||
paths=$(printf '%s' "$HOST_STATS_DIRS" | tr ': ' '\n' | sed '/^$/d')
|
||||
while IFS= read -r d; do
|
||||
[ -n "$d" ] || continue
|
||||
if [ -d "$d" ] || mkdir -p "$d" 2>/dev/null; then
|
||||
if cp -f "$src_stats" "${d}/stats.json" 2>/dev/null; then
|
||||
chmod a+r "${d}/stats.json" 2>/dev/null || true
|
||||
[ -n "$src_run" ] && [ -f "$src_run" ] && cp -f "$src_run" "${d}/stats-run.json" 2>/dev/null || true
|
||||
log "host: published ${d}/stats.json"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
if command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
|
||||
sudo mkdir -p "$d" 2>/dev/null || true
|
||||
sudo cp -f "$src_stats" "${d}/stats.json"
|
||||
sudo chmod a+r "${d}/stats.json" 2>/dev/null || true
|
||||
[ -n "$src_run" ] && [ -f "$src_run" ] && sudo cp -f "$src_run" "${d}/stats-run.json" 2>/dev/null || true
|
||||
log "host: published ${d}/stats.json (sudo)"
|
||||
else
|
||||
log "WARN: cannot write ${d}/stats.json (need write or passwordless sudo)"
|
||||
fi
|
||||
done <<<"$paths"
|
||||
}
|
||||
|
||||
read_pass() {
|
||||
local name="$1" f
|
||||
for f in \
|
||||
|
|
@ -107,11 +175,25 @@ read_pass() {
|
|||
return 0
|
||||
fi
|
||||
done
|
||||
# container (hernani can podman exec root files inside owned containers)
|
||||
if podman inspect -f '{{.State.Running}}' "$BANK_CTR" 2>/dev/null | grep -qx true; then
|
||||
if podman exec "$BANK_CTR" test -r "/root/${name}" 2>/dev/null; then
|
||||
podman exec "$BANK_CTR" cat "/root/${name}" 2>/dev/null | tr -d '\n'
|
||||
return 0
|
||||
# container secrets: /root/… (GOA) or /data/secrets/… (FrancPaysan stage)
|
||||
if [ -n "${BANK_CTR:-}" ] && podman inspect -f '{{.State.Running}}' "$BANK_CTR" 2>/dev/null | grep -qx true; then
|
||||
for path in "/root/${name}" "/data/secrets/${name}" "/data/secrets/bank-admin-password.txt"; do
|
||||
case "$path" in
|
||||
*bank-admin-password.txt)
|
||||
[ "$name" = "bank-admin-password.txt" ] || continue
|
||||
;;
|
||||
esac
|
||||
if podman exec "$BANK_CTR" test -r "$path" 2>/dev/null; then
|
||||
podman exec "$BANK_CTR" cat "$path" 2>/dev/null | tr -d '\n\r'
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
# stage explorer secret naming
|
||||
if [ "$name" = "bank-explorer-password.txt" ]; then
|
||||
if podman exec "$BANK_CTR" test -r /data/secrets/bank-explorer-password.txt 2>/dev/null; then
|
||||
podman exec "$BANK_CTR" cat /data/secrets/bank-explorer-password.txt 2>/dev/null | tr -d '\n\r'
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
|
|
@ -123,6 +205,8 @@ ctr_running() {
|
|||
|
||||
publish_json() {
|
||||
local ctr="$1" dest_dir="$2" src_stats="$3" src_run="$4"
|
||||
[ "${PUBLISH_PODMAN}" = "1" ] || return 0
|
||||
[ -n "$ctr" ] || return 0
|
||||
if ! ctr_running "$ctr"; then
|
||||
log "WARN: $ctr not running — skip publish $dest_dir"
|
||||
return 1
|
||||
|
|
@ -166,6 +250,7 @@ collect_bank() {
|
|||
BANK_URL="$BANK_URL" \
|
||||
BANK_PUBLIC_URL="$BANK_PUBLIC_URL" \
|
||||
EXCHANGE_CONFIG_URL="$EXCHANGE_CONFIG_URL" \
|
||||
BANK_CURRENCY="${BANK_CURRENCY:-}" \
|
||||
BANK_ADMIN_PASS="$admin_pass" \
|
||||
BANK_EXPLORER_PASS="$explorer_pass" \
|
||||
DEMO_DIR="${demo_dir}" \
|
||||
|
|
@ -173,6 +258,7 @@ collect_bank() {
|
|||
TX_PAGE="${TX_PAGE:-500}" \
|
||||
MAX_TX_PAGES="${MAX_TX_PAGES:-0}" \
|
||||
ACCOUNTS_DELTA="${ACCOUNTS_DELTA:--10000}" \
|
||||
STATS_SOURCE_LABEL="$STATS_SOURCE_LABEL" \
|
||||
"$PY" "$LIB/collect_bank_stats.py" \
|
||||
--out "$WORKDIR/bank-stats.json" \
|
||||
--run-out "$WORKDIR/bank-stats-run.json" \
|
||||
|
|
@ -181,11 +267,13 @@ collect_bank() {
|
|||
set -e
|
||||
|
||||
if [ "$ec_bank" -eq 0 ] && [ -f "$WORKDIR/bank-stats.json" ]; then
|
||||
# Always attach in-container RSS/loadavg (not host /proc)
|
||||
# Optional: attach in-container RSS/loadavg (not host /proc)
|
||||
merge_container_resources bank "$BANK_CTR" "$WORKDIR/bank-stats.json" || true
|
||||
publish_json "$BANK_CTR" "$BANK_LANDING_IN" \
|
||||
"$WORKDIR/bank-stats.json" "$WORKDIR/bank-stats-run.json"
|
||||
log "bank: OK → ${BANK_CTR}:${BANK_LANDING_IN}/stats.json"
|
||||
# Shared public feed: same bank stats on all HOST_STATS_DIRS (bank/ex/merchant landings)
|
||||
publish_host_stats "$WORKDIR/bank-stats.json" "$WORKDIR/bank-stats-run.json"
|
||||
log "bank: OK (podman=${BANK_CTR:-none} host_dirs=${HOST_STATS_DIRS:-none})"
|
||||
else
|
||||
log "ERROR: bank collect failed (ec=$ec_bank) — previous stats.json kept"
|
||||
[ -f "$WORKDIR/bank-stats-run.json" ] || \
|
||||
|
|
@ -272,13 +360,26 @@ collect_merchant() {
|
|||
|
||||
# ---------------------------------------------------------------------------
|
||||
main() {
|
||||
log "=== taler-landing-stats start (user=$(id -un) lib=$LIB) ==="
|
||||
collect_bank || ec_bank=$?
|
||||
collect_exchange || ec_ex=$?
|
||||
collect_merchant || ec_mer=$?
|
||||
log "=== taler-landing-stats start (user=$(id -un) lib=$LIB currency=${BANK_CURRENCY:-auto}) ==="
|
||||
log "phases bank=${COLLECT_BANK} exchange=${COLLECT_EXCHANGE} merchant=${COLLECT_MERCHANT} resources=${COLLECT_RESOURCES}"
|
||||
if [ "${COLLECT_BANK}" = "1" ]; then
|
||||
collect_bank || ec_bank=$?
|
||||
else
|
||||
log "skip bank collect"
|
||||
fi
|
||||
if [ "${COLLECT_EXCHANGE}" = "1" ]; then
|
||||
collect_exchange || ec_ex=$?
|
||||
else
|
||||
log "skip exchange collect"
|
||||
fi
|
||||
if [ "${COLLECT_MERCHANT}" = "1" ]; then
|
||||
collect_merchant || ec_mer=$?
|
||||
else
|
||||
log "skip merchant collect"
|
||||
fi
|
||||
log "=== done bank=$ec_bank exchange=$ec_ex merchant=$ec_mer ==="
|
||||
# non-zero if bank failed (primary public flow numbers); soft on ex/mer
|
||||
if [ "$ec_bank" -ne 0 ]; then
|
||||
if [ "${COLLECT_BANK}" = "1" ] && [ "$ec_bank" -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue