Multi-phase global SUMMARY, warn-filter dimmed context, SUMMARY chrome as meta; FP stage host-agent via francpaysan-stage-user (stagepaysan); monpages GOA + FP stage.
368 lines
13 KiB
Bash
Executable file
368 lines
13 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ensure-apt-deploy-test-containers.sh
|
|
#
|
|
# Four rootless podman containers on koopa (no nginx / no HTTPS):
|
|
#
|
|
# Fresh rebuild when repo fingerprint changes:
|
|
# koopa-taler-deploy-test-apt-src-trixie
|
|
# koopa-taler-deploy-test-apt-src-trixie-testing
|
|
#
|
|
# Persistent upgrade path (mytops upgrade.sh style):
|
|
# koopa-taler-deploy-test-apt-src-trixie-upgrade
|
|
# koopa-taler-deploy-test-apt-src-trixie-testing-upgrade
|
|
#
|
|
# Run as hernani on koopa. From elsewhere use run-aptdeploy.sh (ssh $DEPLOY_SSH or $APT_DEPLOY_SSH).
|
|
#
|
|
set -euo pipefail
|
|
|
|
PODMAN_BIN="${APT_DEPLOY_PODMAN:-podman}"
|
|
IMG="${APT_DEPLOY_BASE_IMAGE:-docker.io/library/debian:trixie}"
|
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/taler-apt-deploy"
|
|
URI_BASE="${APT_DEPLOY_URI:-https://deb.taler.net/apt/debian}"
|
|
mkdir -p "$STATE_DIR"
|
|
|
|
# Packages we track for "new version → rebuild fresh containers"
|
|
TRACK_PKGS="${APT_DEPLOY_TRACK_PKGS:-taler-merchant libtalermerchant libtalerexchange libdonau}"
|
|
|
|
# Canonical four containers (keep). Everything else matching legacy names is removed.
|
|
CANONICAL_CONTAINERS=(
|
|
koopa-taler-deploy-test-apt-src-trixie
|
|
koopa-taler-deploy-test-apt-src-trixie-testing
|
|
koopa-taler-deploy-test-apt-src-trixie-upgrade
|
|
koopa-taler-deploy-test-apt-src-trixie-testing-upgrade
|
|
)
|
|
|
|
# Legacy / typo names from earlier iterations — always delete if present
|
|
LEGACY_CONTAINERS=(
|
|
koopa-taler-build-test-apt-trixie
|
|
koopa-taler-build-test-apt-trixie-testing
|
|
koopa-taler-deploy-test-apt-trixie
|
|
koopa-taler-deploy-test-apt-trixie-testing
|
|
koopa-taler-buidl-test-apt-trixie
|
|
koopa-taler-buidl-test-apt-trixie-testing
|
|
)
|
|
|
|
cleanup_legacy() {
|
|
local n img
|
|
echo "======== cleanup legacy containers / images ========"
|
|
for n in "${LEGACY_CONTAINERS[@]}"; do
|
|
if "$PODMAN_BIN" container exists "$n" 2>/dev/null; then
|
|
echo "remove legacy container $n"
|
|
"$PODMAN_BIN" rm -f "$n" 2>/dev/null || true
|
|
fi
|
|
# old setup images for that name
|
|
if "$PODMAN_BIN" image exists "localhost/${n}:setup" 2>/dev/null; then
|
|
echo "remove legacy image localhost/${n}:setup"
|
|
"$PODMAN_BIN" rmi -f "localhost/${n}:setup" 2>/dev/null || true
|
|
fi
|
|
done
|
|
# dangling setup images not in canonical set
|
|
while read -r img; do
|
|
[ -n "$img" ] || continue
|
|
local base keep=0
|
|
base=${img#localhost/}
|
|
base=${base%:setup}
|
|
for n in "${CANONICAL_CONTAINERS[@]}"; do
|
|
[ "$base" = "$n" ] && keep=1 && break
|
|
done
|
|
if [ "$keep" -eq 0 ] && [[ "$img" == localhost/koopa-taler-*-test-apt* ]]; then
|
|
echo "remove non-canonical image $img"
|
|
"$PODMAN_BIN" rmi -f "$img" 2>/dev/null || true
|
|
fi
|
|
done < <("$PODMAN_BIN" images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep -E 'localhost/koopa-taler-.*-test-apt' || true)
|
|
}
|
|
|
|
# Prefer single suite tree: ~/src/taler-monitoring vs ~/taler-monitoring
|
|
# (legacy koopa-admin-log paths are not used). Keep the newer git clone; symlink the other.
|
|
dedupe_suite_repos() {
|
|
local a="$HOME/src/taler-monitoring" b="$HOME/taler-monitoring"
|
|
local ta tb
|
|
[ "${APT_DEPLOY_DEDUPE_REPOS:-1}" = "1" ] || return 0
|
|
if [ -d "$a/.git" ] && [ -d "$b/.git" ] && [ ! -L "$a" ] && [ ! -L "$b" ] \
|
|
&& [ "$(readlink -f "$a" 2>/dev/null || echo "$a")" != "$(readlink -f "$b" 2>/dev/null || echo "$b")" ]; then
|
|
ta=$(git -C "$a" log -1 --format=%ct 2>/dev/null || echo 0)
|
|
tb=$(git -C "$b" log -1 --format=%ct 2>/dev/null || echo 0)
|
|
echo "======== dedupe suite repos (both are git clones) ========"
|
|
echo " $a HEAD_ts=$ta"
|
|
echo " $b HEAD_ts=$tb"
|
|
if [ "$ta" -ge "$tb" ]; then
|
|
echo "keep $a · remove $b · symlink $b → $a"
|
|
rm -rf "$b"
|
|
ln -sfn "$a" "$b"
|
|
else
|
|
echo "keep $b · remove $a · symlink $a → $b"
|
|
rm -rf "$a"
|
|
mkdir -p "$(dirname "$a")"
|
|
ln -sfn "$b" "$a"
|
|
fi
|
|
elif [ -d "$a/.git" ] && [ ! -e "$b" ]; then
|
|
ln -sfn "$a" "$b"
|
|
elif [ -d "$b/.git" ] && [ ! -e "$a" ]; then
|
|
mkdir -p "$(dirname "$a")"
|
|
ln -sfn "$b" "$a"
|
|
fi
|
|
}
|
|
|
|
cleanup_legacy
|
|
dedupe_suite_repos
|
|
# ---------------------------------------------------------------------------
|
|
# Repo fingerprint: Version: lines from suite Packages index
|
|
# ---------------------------------------------------------------------------
|
|
repo_pkg_version() {
|
|
local suite=$1 pkg=$2
|
|
curl -fsS --max-time 45 \
|
|
"${URI_BASE}/dists/${suite}/main/binary-amd64/Packages" 2>/dev/null \
|
|
| awk -v p="$pkg" '
|
|
$0 == "Package: " p { want=1; next }
|
|
/^Package: / { want=0 }
|
|
want && /^Version: / { print $2; exit }
|
|
'
|
|
}
|
|
|
|
repo_fingerprint() {
|
|
local suite=$1 pkg v line=""
|
|
for pkg in $TRACK_PKGS; do
|
|
v=$(repo_pkg_version "$suite" "$pkg" || true)
|
|
[ -n "$v" ] || v="?"
|
|
line="${line}${pkg}=${v};"
|
|
done
|
|
printf '%s' "$line"
|
|
}
|
|
|
|
container_fingerprint() {
|
|
local name=$1
|
|
"$PODMAN_BIN" exec "$name" bash -lc '
|
|
for p in '"$TRACK_PKGS"'; do
|
|
v=$(dpkg-query -W -f="\${Version}" "$p" 2>/dev/null || echo missing)
|
|
printf "%s=%s;" "$p" "$v"
|
|
done
|
|
' 2>/dev/null || echo "missing"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Bootstrap: install base + taler source + packages (shared)
|
|
# ---------------------------------------------------------------------------
|
|
install_base_and_merchant() {
|
|
local name=$1 suite=$2 srcfile=$3
|
|
"$PODMAN_BIN" exec -u root "$name" bash -lc '
|
|
set -euo pipefail
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update -qq
|
|
apt-get install -y -qq ca-certificates curl wget gnupg systemd systemd-sysv \
|
|
dbus procps iproute2
|
|
'
|
|
"$PODMAN_BIN" exec -u root "$name" bash -lc "
|
|
set -euo pipefail
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
mkdir -p /etc/apt/keyrings
|
|
wget -q -O /etc/apt/keyrings/taler-systems.gpg https://taler.net/taler-systems.gpg
|
|
cat >/etc/apt/sources.list.d/${srcfile} <<EOF
|
|
Architectures: amd64
|
|
Components: main
|
|
X-Repolib-Name: Taler
|
|
Signed-By: /etc/apt/keyrings/taler-systems.gpg
|
|
Suites: ${suite}
|
|
Types: deb
|
|
URIs: ${URI_BASE}
|
|
EOF
|
|
apt-get update -qq
|
|
apt-get install -y \
|
|
taler-merchant \
|
|
taler-merchant-typst \
|
|
taler-merchant-webui \
|
|
taler-terms-generator \
|
|
postgresql \
|
|
s-nail \
|
|
jq
|
|
"
|
|
}
|
|
|
|
commit_and_systemd() {
|
|
local name=$1
|
|
"$PODMAN_BIN" stop "$name"
|
|
"$PODMAN_BIN" commit "$name" "localhost/${name}:setup"
|
|
"$PODMAN_BIN" rm "$name"
|
|
"$PODMAN_BIN" run -d --name "$name" \
|
|
--hostname "$name" \
|
|
--systemd=always \
|
|
--cgroupns=host \
|
|
"localhost/${name}:setup" \
|
|
/lib/systemd/systemd
|
|
sleep 5
|
|
# enable merchant stack if unit exists
|
|
"$PODMAN_BIN" exec "$name" bash -lc '
|
|
set +e
|
|
systemctl daemon-reload 2>/dev/null
|
|
systemctl enable taler-merchant.target 2>/dev/null
|
|
systemctl start taler-merchant.target 2>/dev/null
|
|
sleep 2
|
|
systemctl is-active taler-merchant-httpd.service 2>/dev/null || true
|
|
' || true
|
|
}
|
|
|
|
create_from_scratch() {
|
|
local name=$1 suite=$2 srcfile=$3
|
|
echo "======== CREATE (fresh) $name Suites: $suite ========"
|
|
"$PODMAN_BIN" pull "$IMG" >/dev/null
|
|
"$PODMAN_BIN" rm -f "$name" 2>/dev/null || true
|
|
# drop previous setup image so we do not accumulate duplicates
|
|
"$PODMAN_BIN" rmi -f "localhost/${name}:setup" 2>/dev/null || true
|
|
"$PODMAN_BIN" run -d --name "$name" --hostname "$name" "$IMG" sleep infinity
|
|
install_base_and_merchant "$name" "$suite" "$srcfile"
|
|
commit_and_systemd "$name"
|
|
printf '%s\n' "$(repo_fingerprint "$suite")" >"$STATE_DIR/${name}.fp"
|
|
printf '%s\n' "$(container_fingerprint "$name")" >"$STATE_DIR/${name}.installed"
|
|
echo "created $name fp=$(cat "$STATE_DIR/${name}.fp")"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Upgrade path (mytops-admin-log upgrade.sh merchant packages, no restic/nginx)
|
|
# Prints a markdown-ish table of before → after versions.
|
|
# ---------------------------------------------------------------------------
|
|
upgrade_inside() {
|
|
local name=$1
|
|
local before after pkg v0 v1
|
|
echo "======== UPGRADE $name (mytops-style apt install + upgrade) ========"
|
|
|
|
before=$("$PODMAN_BIN" exec "$name" bash -lc '
|
|
for p in taler-merchant libtalermerchant libtalerexchange libdonau libgnunet \
|
|
taler-merchant-webui taler-merchant-typst taler-terms-generator; do
|
|
v=$(dpkg-query -W -f="\${Version}" "$p" 2>/dev/null || echo "-")
|
|
printf "%s %s\n" "$p" "$v"
|
|
done
|
|
' 2>/dev/null || true)
|
|
|
|
# capture upgrade output
|
|
set +e
|
|
"$PODMAN_BIN" exec -u root "$name" bash -lc '
|
|
set -euo pipefail
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
systemctl stop taler-merchant.target 2>/dev/null || true
|
|
systemctl stop taler-merchant-donaukeyupdate.service 2>/dev/null || true
|
|
apt-get update -qq
|
|
apt-get install -y \
|
|
taler-merchant \
|
|
taler-merchant-typst \
|
|
taler-merchant-webui \
|
|
taler-terms-generator \
|
|
|| true
|
|
if dpkg -l challenger-httpd 2>/dev/null | grep -q "^ii"; then
|
|
apt-get remove -y challenger-httpd || true
|
|
fi
|
|
apt-get upgrade -y
|
|
systemctl daemon-reload 2>/dev/null || true
|
|
systemctl enable taler-merchant.target 2>/dev/null || true
|
|
# try dbconfig if present (may no-op without full config)
|
|
command -v taler-merchant-dbconfig >/dev/null && taler-merchant-dbconfig 2>/dev/null || true
|
|
systemctl reset-failed "taler-merchant*" 2>/dev/null || true
|
|
systemctl start taler-merchant.target 2>/dev/null || true
|
|
sleep 2
|
|
' 2>&1 | tee "$STATE_DIR/${name}.upgrade.log" | tail -30
|
|
set -e
|
|
|
|
after=$("$PODMAN_BIN" exec "$name" bash -lc '
|
|
for p in taler-merchant libtalermerchant libtalerexchange libdonau libgnunet \
|
|
taler-merchant-webui taler-merchant-typst taler-terms-generator; do
|
|
v=$(dpkg-query -W -f="\${Version}" "$p" 2>/dev/null || echo "-")
|
|
printf "%s %s\n" "$p" "$v"
|
|
done
|
|
' 2>/dev/null || true)
|
|
|
|
echo
|
|
echo "| package | before | after | change |"
|
|
echo "|---------|--------|-------|--------|"
|
|
while read -r pkg v0; do
|
|
[ -n "${pkg:-}" ] || continue
|
|
v1=$(printf '%s\n' "$after" | awk -v p="$pkg" '$1==p {print $2; exit}')
|
|
v1=${v1:--}
|
|
if [ "$v0" = "$v1" ]; then
|
|
ch="="
|
|
else
|
|
ch="UPGRADED"
|
|
fi
|
|
echo "| $pkg | $v0 | $v1 | $ch |"
|
|
done <<<"$before"
|
|
|
|
printf '%s\n' "$(container_fingerprint "$name")" >"$STATE_DIR/${name}.installed"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fresh mode: recreate if missing / not running / repo fp changed
|
|
# ---------------------------------------------------------------------------
|
|
ensure_fresh() {
|
|
local name=$1 suite=$2 srcfile=$3
|
|
local want have running
|
|
|
|
want=$(repo_fingerprint "$suite")
|
|
echo "repo fingerprint [$suite]: $want"
|
|
|
|
if ! "$PODMAN_BIN" container exists "$name" 2>/dev/null; then
|
|
create_from_scratch "$name" "$suite" "$srcfile"
|
|
return
|
|
fi
|
|
|
|
running=$("$PODMAN_BIN" inspect -f '{{.State.Running}}' "$name" 2>/dev/null || echo false)
|
|
have=$(container_fingerprint "$name")
|
|
echo "installed in $name: $have"
|
|
|
|
if [ "$running" != "true" ] \
|
|
|| ! "$PODMAN_BIN" exec "$name" bash -lc 'command -v taler-merchant-httpd' >/dev/null 2>&1 \
|
|
|| [ "$want" != "$have" ]; then
|
|
echo "rebuild $name (running=$running fp_match=$([ "$want" = "$have" ] && echo yes || echo NO))"
|
|
create_from_scratch "$name" "$suite" "$srcfile"
|
|
else
|
|
echo "OK $name up-to-date (fresh container matches repo)"
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Upgrade mode: create once, then always upgrade_inside
|
|
# ---------------------------------------------------------------------------
|
|
ensure_upgrade() {
|
|
local name=$1 suite=$2 srcfile=$3
|
|
local running
|
|
|
|
if ! "$PODMAN_BIN" container exists "$name" 2>/dev/null; then
|
|
create_from_scratch "$name" "$suite" "$srcfile"
|
|
echo "(initial install for upgrade-track container $name)"
|
|
return
|
|
fi
|
|
|
|
running=$("$PODMAN_BIN" inspect -f '{{.State.Running}}' "$name" 2>/dev/null || echo false)
|
|
if [ "$running" != "true" ]; then
|
|
echo "start $name"
|
|
"$PODMAN_BIN" start "$name" || {
|
|
echo "recreate broken $name"
|
|
create_from_scratch "$name" "$suite" "$srcfile"
|
|
return
|
|
}
|
|
sleep 3
|
|
fi
|
|
|
|
if ! "$PODMAN_BIN" exec "$name" bash -lc 'command -v taler-merchant-httpd' >/dev/null 2>&1; then
|
|
echo "recreate $name (no httpd after start)"
|
|
create_from_scratch "$name" "$suite" "$srcfile"
|
|
return
|
|
fi
|
|
|
|
upgrade_inside "$name"
|
|
}
|
|
|
|
MODE="${1:-all}" # all | fresh | upgrade
|
|
|
|
case "$MODE" in
|
|
all|fresh)
|
|
ensure_fresh koopa-taler-deploy-test-apt-src-trixie trixie taler.sources
|
|
ensure_fresh koopa-taler-deploy-test-apt-src-trixie-testing trixie-testing taler-testing.sources
|
|
;;&
|
|
all|upgrade)
|
|
ensure_upgrade koopa-taler-deploy-test-apt-src-trixie-upgrade trixie taler.sources
|
|
ensure_upgrade koopa-taler-deploy-test-apt-src-trixie-testing-upgrade trixie-testing taler-testing.sources
|
|
;;
|
|
*)
|
|
echo "usage: $0 [all|fresh|upgrade]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
echo "done ensure-apt-deploy-test-containers mode=$MODE"
|