diff --git a/ops/restore-tests/run-restore-checks.sh b/ops/restore-tests/run-restore-checks.sh index ae6e239..30536ed 100755 --- a/ops/restore-tests/run-restore-checks.sh +++ b/ops/restore-tests/run-restore-checks.sh @@ -58,6 +58,12 @@ case "$MODE" in fi exec "$SCRIPT_DIR/komodo-mongo-restore-test.sh" ;; + traefik) + if [ "$WHATIF" = "--what-if" ]; then + exec "$SCRIPT_DIR/traefik-restore-test.sh" --what-if + fi + exec "$SCRIPT_DIR/traefik-restore-test.sh" + ;; mailarchiver) if [ "$WHATIF" = "--what-if" ]; then exec "$SCRIPT_DIR/mailarchiver-restore-test.sh" --what-if diff --git a/ops/restore-tests/traefik-compose.test.yml b/ops/restore-tests/traefik-compose.test.yml new file mode 100644 index 0000000..ee48d9c --- /dev/null +++ b/ops/restore-tests/traefik-compose.test.yml @@ -0,0 +1,29 @@ +services: + restoretest-traefik: + image: traefik:v3.7@sha256:6b9cbca6fac42ab0075f5437d8dc1685cfd188626d8d515839ea94f8b6271c42 + container_name: restoretest-traefik + restart: "no" + command: + # Minimale Config fuer den Smoke: kein Docker-Provider (kein docker.sock), + # kein ACME (kein CF-Token), kein TLS-Entrypoint. Nur: + # - File-Provider mit restauriertem dynamic/-Verzeichnis + # - HTTP-Entrypoint auf Test-Port + # - Ping-Endpoint fuer Health-Check + - --ping=true + - --ping.entrypoint=web + - --providers.file.directory=/dynamic + - --providers.file.watch=false + - --entrypoints.web.address=:80 + - --api.dashboard=false + - --log.level=INFO + ports: + - "127.0.0.1:18880:80" + volumes: + # Restauriertes dynamic/-Verzeichnis aus Borg (read-only) + - /mnt/user/backups/restore-lab/traefik/dynamic:/dynamic:ro + # Restauriertes letsencrypt/ fuer Existenz-Nachweis (read-only) + - /mnt/user/backups/restore-lab/traefik/letsencrypt:/letsencrypt:ro + # KEIN docker.sock — Test-Traefik darf keine produktiven Container discovern + # KEIN CF-Token — keine DNS-Challenge im Smoke + security_opt: + - no-new-privileges:true diff --git a/ops/restore-tests/traefik-restore-test.sh b/ops/restore-tests/traefik-restore-test.sh new file mode 100644 index 0000000..e5f48bf --- /dev/null +++ b/ops/restore-tests/traefik-restore-test.sh @@ -0,0 +1,153 @@ +#!/bin/bash +set -euo pipefail + +# Traefik Restore Smoke Test +# +# Beweist, dass die restaurierten Traefik-Dateien (dynamic/, letsencrypt/) +# aus dem Borg-Archiv einen funktionsfaehigen Traefik-Start ermoeglichen. +# +# Scope: +# - Borg-Extract von dynamic/ und letsencrypt/ +# - Traefik startet mit File-Provider gegen restauriertes dynamic/ +# - /ping Health-Endpoint antwortet +# - acme.json aus letsencrypt/ ist vorhanden und nicht leer +# +# Bewusst NICHT Teil des Smokes: +# - Docker-Provider (kein docker.sock im Test — wuerde produktive Container discovern) +# - ACME/DNS-Challenge (kein CF-Token im Test) +# - TLS-Terminierung (kein Cert-Test) +# - Produktive Ports 80/443 + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +. "$SCRIPT_DIR/common.sh" + +WHATIF=0 +KEEP_DATA=0 +for arg in "$@"; do + case "$arg" in + --what-if) WHATIF=1 ;; + --keep-data) KEEP_DATA=1 ;; + *) echo "Unknown argument: $arg" >&2; exit 1 ;; + esac +done + +RESTORE_ROOT="/mnt/user/backups/restore-lab/traefik" +REPORT_ROOT="/mnt/user/backups/restore-reports" +EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/traefik-extract" +COMPOSE_FILE="$SCRIPT_DIR/traefik-compose.test.yml" +REPORT_FILE="$REPORT_ROOT/traefik-$(date +%F).md" + +if [ "$WHATIF" -eq 1 ]; then + cat <&2 + exit 1 +fi + +# Stufe 1: Traefik-Dateien aus Borg (dynamic/ + letsencrypt/) +# Secrets/ wird bewusst NICHT extrahiert (enthaelt CF-Token) +borg_extract "/restore/traefik-extract" \ + "local/appdata/traefik/dynamic" \ + "local/appdata/traefik/letsencrypt" + +if [ ! -d "$EXTRACT_DIR/local/appdata/traefik/dynamic" ]; then + echo "Traefik dynamic/ path missing in Borg archive" >&2 + exit 1 +fi + +cp -a "$EXTRACT_DIR/local/appdata/traefik/dynamic" "$RESTORE_ROOT/dynamic" +cp -a "$EXTRACT_DIR/local/appdata/traefik/letsencrypt" "$RESTORE_ROOT/letsencrypt" +chmod -R a+rX "$RESTORE_ROOT/dynamic" "$RESTORE_ROOT/letsencrypt" + +# Stufe 2: Datei-Checks +dynamic_files="$(find "$RESTORE_ROOT/dynamic" -type f | wc -l)" +acme_size="$(stat -c %s "$RESTORE_ROOT/letsencrypt/acme.json" 2>/dev/null || echo 0)" + +# Stufe 3: Traefik starten +docker compose -f "$COMPOSE_FILE" up -d restoretest-traefik >/dev/null + +http_status="" +for _ in $(seq 1 30); do + http_status="$(curl -s -o /dev/null -w '%{http_code}' \ + http://127.0.0.1:18880/ping || true)" + if [ "$http_status" = "200" ]; then + break + fi + sleep 2 +done + +if [ "$http_status" != "200" ]; then + echo "Traefik /ping smoke failed: status=$http_status" >&2 + docker logs --tail 60 restoretest-traefik >&2 || true + exit 1 +fi + +write_report "$REPORT_FILE" < $REPORT_FILE"