ops: GOA merchant-first 2026-09-15 daylog + lab-hosts + APPLY driver

This commit is contained in:
Hernâni Marques 2026-09-16 02:48:57 +02:00
parent 2eb44c95d9
commit 0d881cd726
No known key found for this signature in database
GPG key ID: CB5738652768F7E9
3 changed files with 468 additions and 0 deletions

View file

@ -0,0 +1,325 @@
#!/usr/bin/env bash
# Live GOA: dry-run → backup → apt upgrade (bank/exchange/merchant) → podman commit.
# Host: koopa (hernani, podman). No Freigabe-bypass — caller must have Freigabe.
#
# ./goa-live-backup-upgrade.sh dry-run
# ./goa-live-backup-upgrade.sh apply # backup + apt + commit + smoke
# ./goa-live-backup-upgrade.sh status
#
# State/log: ~/backups/goa/upgrade-runs/<stamp>/
# Mirror: koopa-admin-log/scripts/taler-shared/
set -euo pipefail
BANK_CTR="${BANK_CTR:-taler-hacktivism-bank}"
EXCHANGE_CTR="${EXCHANGE_CTR:-taler-hacktivism-exchange-ansible}"
MERCHANT_CTR="${MERCHANT_CTR:-taler-hacktivism}"
BANK_IMG_LIVE="${BANK_IMG_LIVE:-localhost/taler-hacktivism-banking:live}"
EXCHANGE_IMG_LIVE="${EXCHANGE_IMG_LIVE:-localhost/taler-hacktivism-exchange-ansible:landing}"
MERCHANT_IMG_LIVE="${MERCHANT_IMG_LIVE:-localhost/taler-hacktivism-live:landing}"
STAMP="${STAMP:-$(date -u +%Y%m%dT%H%MZ)}"
ROOT="${ROOT:-$HOME/backups/goa}"
RUN_DIR="${RUN_DIR:-$ROOT/upgrade-runs/$STAMP}"
MODE="${1:-dry-run}"
mkdir -p "$RUN_DIR"
LOG="$RUN_DIR/run.log"
STATUS="$RUN_DIR/STATUS"
exec > >(tee -a "$LOG") 2>&1
log() { printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"; }
set_status() {
printf 'STATUS=%s\nSTAMP=%s\nWHEN=%s\nMODE=%s\n' "$1" "$STAMP" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$MODE" >"$STATUS"
shift || true
for kv in "$@"; do printf '%s\n' "$kv" >>"$STATUS"; done
ln -sfn "$STAMP" "$ROOT/upgrade-runs/current" 2>/dev/null || true
ln -sfn "upgrade-runs/$STAMP" "$ROOT/upgrade-current-run" 2>/dev/null || true
}
pod_root() {
local ctr="$1"; shift
podman exec -u root "$ctr" bash -lc "$*"
}
dry_one() {
local name="$1" ctr="$2" pkgs="$3"
log "=== DRY $name ($ctr) ==="
pod_root "$ctr" "
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
echo '--- installed ---'
dpkg -l $pkgs 2>/dev/null | awk '/^ii/{print \$2,\$3}' || true
echo '--- candidates (policy sample) ---'
for p in $pkgs; do
apt-cache policy \"\$p\" 2>/dev/null | awk -v p=\"\$p\" 'NR<=6{print p\": \"\$0}'
done
echo '--- simulate install ---'
apt-get -s install -y $pkgs 2>&1 | egrep -i '^(Inst |Remv |Conf |The following packages)' || true
"
}
cmd_dry_run() {
set_status DRY_RUN_RUNNING
log "RUN_DIR=$RUN_DIR"
df -h "$HOME" | tee "$RUN_DIR/df.txt"
podman ps --format '{{.Names}} {{.Status}} {{.Image}}' | grep -E 'taler-hacktivism|goa-regio' | tee "$RUN_DIR/ctrs.txt" || true
dry_one bank "$BANK_CTR" "libeufin-bank libeufin-common"
dry_one exchange "$EXCHANGE_CTR" "taler-exchange taler-exchange-database taler-exchange-offline libtalerexchange taler-terms-generator"
dry_one merchant "$MERCHANT_CTR" "taler-merchant taler-merchant-webui libtalermerchant taler-terms-generator"
cat >"$RUN_DIR/PLAN.txt" <<EOF
PLAN (apply):
1. Backup stamp $STAMP under $ROOT/$STAMP + current symlink
- bank: etc-libeufin + PG dumps (libeufinbank/libeufin)
- exchange: etc-taler-exchange + offline/master.priv + taler-exchange.dump
- merchant: etc-taler + merchant DB dump
2. apt-get install (real) via upgrade-goa-debs logic + start_*.sh restarts
3. podman commit each ctr → :upgraded-$STAMP then retag live/landing
NOTE: no Containerfile rebuild (would wipe overlay). Commit = new image with newer debs.
4. smoke /config on :9010 :9011 :9012 + public FQDNs
5. leave STATUS=APPLY_OK for laptop elelmon recheck
EOF
log "PLAN written $RUN_DIR/PLAN.txt"
set_status DRY_RUN_OK "NEXT=apply" "LOG=$LOG"
log "DRY_RUN_OK"
}
backup_ctr_bank() {
local dest="$1/bank"
mkdir -p "$dest"
pod_root "$BANK_CTR" '
set -e
mkdir -p /tmp/goa-bak
rm -rf /tmp/goa-bak/*
mkdir -p /tmp/goa-bak/etc-libeufin /tmp/goa-bak/var-lib-libeufin-bank
cp -a /etc/libeufin/* /tmp/goa-bak/etc-libeufin/ 2>/dev/null || true
cp -a /var/lib/libeufin-bank/. /tmp/goa-bak/var-lib-libeufin-bank/ 2>/dev/null || true
runuser -u postgres -- psql -lAt > /tmp/goa-bak/pg-databases.txt || true
runuser -u postgres -- pg_dump -Fc libeufinbank > /tmp/goa-bak/libeufinbank.dump || \
runuser -u postgres -- pg_dump -Fc libeufinbank > /tmp/goa-bak/libeufinbank.dump
runuser -u postgres -- pg_dump -Fc libeufin > /tmp/goa-bak/libeufin.dump 2>/dev/null || true
du -sh /tmp/goa-bak/* > /tmp/goa-bak/du.txt || true
tar -C /tmp/goa-bak -czf /tmp/goa-bank-bak.tgz .
'
podman cp "$BANK_CTR:/tmp/goa-bank-bak.tgz" "$dest/goa-bank-bak.tgz"
pod_root "$BANK_CTR" 'rm -rf /tmp/goa-bak /tmp/goa-bank-bak.tgz'
}
backup_ctr_exchange() {
local dest="$1/exchange"
mkdir -p "$dest"
pod_root "$EXCHANGE_CTR" '
set -e
mkdir -p /tmp/goa-bak
rm -rf /tmp/goa-bak/*
mkdir -p /tmp/goa-bak/etc-taler-exchange /tmp/goa-bak/offline
cp -a /etc/taler-exchange/. /tmp/goa-bak/etc-taler-exchange/ 2>/dev/null || true
cp -a /var/lib/taler-exchange/offline/. /tmp/goa-bak/offline/ 2>/dev/null || true
runuser -u postgres -- psql -lAt > /tmp/goa-bak/pg-databases.txt || true
DB=$(runuser -u postgres -- psql -lAt | awk -F"|" "/taler/ {print \$1}" | head -1 | tr -d " ")
[ -n "$DB" ] || DB=taler-exchange
runuser -u postgres -- pg_dump -Fc "$DB" > /tmp/goa-bak/taler-exchange.dump
tar -C /tmp/goa-bak -czf /tmp/goa-exchange-bak.tgz .
'
podman cp "$EXCHANGE_CTR:/tmp/goa-exchange-bak.tgz" "$dest/goa-exchange-bak.tgz"
pod_root "$EXCHANGE_CTR" 'rm -rf /tmp/goa-bak /tmp/goa-exchange-bak.tgz'
}
backup_ctr_merchant() {
local dest="$1/merchant"
mkdir -p "$dest"
pod_root "$MERCHANT_CTR" '
set -e
mkdir -p /tmp/goa-bak
rm -rf /tmp/goa-bak/*
mkdir -p /tmp/goa-bak/etc-taler /tmp/goa-bak/etc-taler-merchant
cp -a /etc/taler/. /tmp/goa-bak/etc-taler/ 2>/dev/null || true
cp -a /etc/taler-merchant/. /tmp/goa-bak/etc-taler-merchant/ 2>/dev/null || true
runuser -u postgres -- psql -lAt > /tmp/goa-bak/pg-databases.txt || true
DB=$(runuser -u postgres -- psql -lAt | awk -F"|" "/merchant|taler/ {print \$1}" | head -1 | tr -d " ")
[ -n "$DB" ] || DB=taler-merchant
runuser -u postgres -- pg_dump -Fc "$DB" > /tmp/goa-bak/taler-merchant.dump
tar -C /tmp/goa-bak -czf /tmp/goa-merchant-bak.tgz .
'
podman cp "$MERCHANT_CTR:/tmp/goa-merchant-bak.tgz" "$dest/goa-merchant-bak.tgz"
pod_root "$MERCHANT_CTR" 'rm -rf /tmp/goa-bak /tmp/goa-merchant-bak.tgz'
}
cmd_backup() {
local bak="$ROOT/$STAMP"
mkdir -p "$bak/meta"
log "BACKUP → $bak"
{
date -R
hostname
podman ps -a --format '{{.Names}} | {{.Image}} | {{.Status}}'
} >"$bak/meta/inventory.txt"
backup_ctr_bank "$bak"
backup_ctr_exchange "$bak"
backup_ctr_merchant "$bak"
du -sh "$bak"/* >"$bak/meta/du.txt" || true
find "$bak" -type f -printf '%s %p\n' >"$bak/meta/manifest-sizes.txt" || true
ln -sfn "$STAMP" "$ROOT/current"
log "BACKUP_OK current→$STAMP"
echo "$bak"
}
upgrade_bank() {
log "======== apt bank ========"
pod_root "$BANK_CTR" '
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y libeufin-bank libeufin-common
dpkg -l libeufin-bank libeufin-common | awk "/^ii/{print \$2,\$3}"
'
pod_root "$BANK_CTR" '
set -e
if ! runuser -u postgres -- psql -c "SELECT 1" >/dev/null 2>&1; then
pg_ctlcluster 17 main start || true
sleep 2
fi
mkdir -p /var/log/libeufin-bank
chown libeufin-bank:libeufin-bank /var/log/libeufin-bank 2>/dev/null || true
runuser -u libeufin-bank -- /usr/local/bin/start_bank.sh --restart
'
}
upgrade_exchange() {
log "======== apt exchange ========"
pod_root "$EXCHANGE_CTR" '
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y \
taler-exchange taler-exchange-database taler-exchange-offline \
taler-exchange-typst libtalerexchange taler-terms-generator \
taler-exchange-aml-webui taler-exchange-kyc-webui || true
dpkg -l "taler-exchange*" "libtalerexchange" "taler-terms-generator" 2>/dev/null \
| awk "/^ii/{print \$2,\$3}"
'
pod_root "$EXCHANGE_CTR" '
set -e
if id taler-exchange-httpd >/dev/null 2>&1; then
runuser -u taler-exchange-httpd -- taler-exchange-dbinit 2>&1 | tail -20 || true
fi
if [ -x /root/start_base_services_for_taler_exchange.sh ]; then
/root/start_base_services_for_taler_exchange.sh --no-shell 2>/dev/null || true
fi
if [ -x /usr/local/bin/start_exchange.sh ]; then
runuser -u taler-exchange-httpd -- /usr/local/bin/start_exchange.sh --restart 2>&1 | tail -30 || true
fi
pgrep -a taler-exchange-httpd | head -3 || true
'
}
upgrade_merchant() {
log "======== apt merchant ========"
pod_root "$MERCHANT_CTR" '
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y \
taler-merchant taler-merchant-webui taler-merchant-typst \
libtalermerchant taler-terms-generator
dpkg -l "taler-merchant*" "libtalermerchant" 2>/dev/null | awk "/^ii/{print \$2,\$3}"
'
pod_root "$MERCHANT_CTR" '
set -e
if id taler-merchant-httpd >/dev/null 2>&1; then
runuser -u taler-merchant-httpd -- taler-merchant-dbinit 2>&1 | tail -20 || true
fi
runuser -u taler-merchant-httpd -- /usr/local/bin/start_merchant.sh --restart
'
}
commit_images() {
log "======== podman commit ========"
local tag="upgraded-$STAMP"
podman commit "$BANK_CTR" "localhost/taler-hacktivism-banking:$tag"
podman tag "localhost/taler-hacktivism-banking:$tag" "$BANK_IMG_LIVE"
podman commit "$EXCHANGE_CTR" "localhost/taler-hacktivism-exchange-ansible:$tag"
podman tag "localhost/taler-hacktivism-exchange-ansible:$tag" "$EXCHANGE_IMG_LIVE"
podman commit "$MERCHANT_CTR" "localhost/taler-hacktivism-live:$tag"
podman tag "localhost/taler-hacktivism-live:$tag" "$MERCHANT_IMG_LIVE"
podman images --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.Size}}' \
| grep -E "upgraded-$STAMP|:live|:landing" | tee "$RUN_DIR/images-after.txt" || true
}
smoke() {
# Return 0=ok / 1=fail. Do NOT echo ok into stdout for capture:
# with exec>tee, sm=$(smoke) would swallow log lines and never equal "1".
log "======== smoke ========"
local ok=1
for pair in "bank:9012:http" "exchange:9011:http" "merchant:9010:https"; do
IFS=: read -r name port sch <<<"$pair"
code=$(curl -skS -m 10 -o /dev/null -w '%{http_code}' "${sch}://127.0.0.1:${port}/config" || echo 000)
log "loopback $name :$port$code"
[ "$code" = "200" ] || ok=0
done
for url in \
https://bank.hacktivism.ch/config \
https://exchange.hacktivism.ch/config \
https://taler.hacktivism.ch/config; do
code=$(curl -skS -m 15 -o /dev/null -w '%{http_code}' "$url" || echo 000)
log "public $url$code"
[ "$code" = "200" ] || ok=0
done
[ "$ok" = "1" ]
}
versions_snap() {
log "======== versions after ========"
pod_root "$BANK_CTR" 'dpkg -l libeufin-bank libeufin-common | awk "/^ii/{print \$2,\$3}"' | tee "$RUN_DIR/ver-bank.txt"
pod_root "$EXCHANGE_CTR" 'dpkg -l "taler-exchange" "libtalerexchange" 2>/dev/null | awk "/^ii/{print \$2,\$3}"' | tee "$RUN_DIR/ver-exchange.txt"
pod_root "$MERCHANT_CTR" 'dpkg -l "taler-merchant" "taler-merchant-webui" "libtalermerchant" 2>/dev/null | awk "/^ii/{print \$2,\$3}"' | tee "$RUN_DIR/ver-merchant.txt"
}
cmd_apply() {
set_status APPLY_RUNNING
cmd_backup
set_status APPLY_BACKUP_OK "BAK=$ROOT/$STAMP"
upgrade_bank
upgrade_exchange
upgrade_merchant
set_status APPLY_APT_OK
commit_images
set_status APPLY_COMMIT_OK
versions_snap
if smoke; then
set_status APPLY_OK "NEXT=elelmon-moderate" "BAK=$ROOT/$STAMP" "LOG=$LOG"
log "APPLY_OK — next: elelmon moderate from laptop"
else
set_status APPLY_SMOKE_FAIL "NEXT=triage-smoke" "BAK=$ROOT/$STAMP" "LOG=$LOG"
log "APPLY_SMOKE_FAIL"
exit 1
fi
}
cmd_status() {
local cur="$ROOT/upgrade-runs/current"
if [ -L "$cur" ] || [ -d "$cur" ]; then
echo "CURRENT_RUN=$(readlink -f "$ROOT/upgrade-runs/current" 2>/dev/null || echo "$cur")"
cat "$ROOT/upgrade-runs/current/STATUS" 2>/dev/null || cat "$cur/STATUS" 2>/dev/null || echo 'no STATUS'
else
echo 'NO_RUN'
fi
ls -la "$ROOT/upgrade-runs/" 2>/dev/null | tail -10 || true
}
case "$MODE" in
dry-run|dry) cmd_dry_run ;;
apply) cmd_apply ;;
status) cmd_status ;;
-h|--help|help)
sed -n '2,16p' "$0"
exit 0
;;
*)
echo "usage: $0 dry-run|apply|status" >&2
exit 2
;;
esac