diff --git a/ops/restore-tests/adguard-compose.test.yml b/ops/restore-tests/adguard-compose.test.yml new file mode 100644 index 0000000..8919535 --- /dev/null +++ b/ops/restore-tests/adguard-compose.test.yml @@ -0,0 +1,14 @@ +services: + restoretest-adguard: + image: adguard/adguardhome:v0.107.76@sha256:7157eb1dc3b26c7af1d6898759a7b3f7d0fa09891fbd2d3caa6abc1057a9179b + container_name: restoretest-adguard + restart: "no" + ports: + - "127.0.0.1:15353:53/tcp" + - "127.0.0.1:15353:53/udp" + - "127.0.0.1:13001:80/tcp" + volumes: + - /mnt/user/backups/restore-lab/adguard/work:/opt/adguardhome/work + - /mnt/user/backups/restore-lab/adguard/conf:/opt/adguardhome/conf + security_opt: + - no-new-privileges:true diff --git a/ops/restore-tests/adguard-restore-test.sh b/ops/restore-tests/adguard-restore-test.sh new file mode 100755 index 0000000..d03f596 --- /dev/null +++ b/ops/restore-tests/adguard-restore-test.sh @@ -0,0 +1,181 @@ +#!/bin/bash +set -euo pipefail + +# AdGuard Home Restore Smoke Test +# +# Scope: +# - Borg-Extract von /local/appdata/adguard/conf +# - YAML-/Strukturcheck fuer AdGuardHome.yaml +# - Start einer isolierten Testinstanz auf localhost-Ports +# - HTTP-Smoke gegen Admin-UI/API +# - DNS-Smoke gegen localhost:15353, falls ein passender Resolver-Client vorhanden ist + +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/adguard" +REPORT_ROOT="/mnt/user/backups/restore-reports" +EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/adguard-extract" +COMPOSE_FILE="$SCRIPT_DIR/adguard-compose.test.yml" +REPORT_FILE="$REPORT_ROOT/adguard-$(date +%F).md" +TEST_HTTP="http://127.0.0.1:13001" +TEST_DNS_PORT="15353" + +if [ "$WHATIF" -eq 1 ]; then + cat <&2 + exit 1 +fi + +borg_extract "/restore/adguard-extract" "local/appdata/adguard/conf" + +if [ ! -d "$EXTRACT_DIR/local/appdata/adguard/conf" ]; then + echo "AdGuard conf path missing in Borg archive" >&2 + exit 1 +fi + +cp -a "$EXTRACT_DIR/local/appdata/adguard/conf/." "$RESTORE_ROOT/conf/" +chmod -R a+rX "$RESTORE_ROOT/conf" +chmod -R a+rwX "$RESTORE_ROOT/work" + +config_file="$RESTORE_ROOT/conf/AdGuardHome.yaml" +if [ ! -s "$config_file" ]; then + echo "Missing restored AdGuardHome.yaml" >&2 + exit 1 +fi + +if command -v ruby >/dev/null 2>&1; then + ruby -e 'require "yaml"; YAML.load_file(ARGV.fetch(0))' "$config_file" + yaml_check="ruby-yaml-ok" +else + grep -q '^dns:' "$config_file" + grep -q '^http:' "$config_file" + yaml_check="basic-structure-ok" +fi + +filter_count="$(grep -c '^[[:space:]]*-[[:space:]]*enabled:' "$config_file" 2>/dev/null || true)" + +docker compose -f "$COMPOSE_FILE" up -d restoretest-adguard >/dev/null + +http_status="" +for _ in $(seq 1 60); do + http_status="$(curl -s -o /tmp/adguard-body.html -w '%{http_code}' \ + "$TEST_HTTP/control/status" || true)" + if [ "$http_status" = "200" ] || [ "$http_status" = "401" ] || [ "$http_status" = "403" ]; then + break + fi + sleep 2 +done + +if [ "$http_status" != "200" ] && [ "$http_status" != "401" ] && [ "$http_status" != "403" ]; then + echo "AdGuard HTTP smoke failed: status=$http_status" >&2 + docker logs --tail 80 restoretest-adguard >&2 || true + exit 1 +fi + +dns_status="not-run" +dns_detail="no dig/drill command available" +if command -v dig >/dev/null 2>&1; then + if dig @127.0.0.1 -p "$TEST_DNS_PORT" git.kaleschke.info A +time=3 +tries=1 >/tmp/adguard-dig.out 2>&1; then + dns_status="ok" + dns_detail="$(grep -E '^[[:alnum:].-]+[[:space:]]+[0-9]+[[:space:]]+IN[[:space:]]+A[[:space:]]+' /tmp/adguard-dig.out | head -1 || true)" + else + dns_status="failed" + dns_detail="$(tail -20 /tmp/adguard-dig.out | tr '\n' ' ')" + fi +elif command -v drill >/dev/null 2>&1; then + if drill -p "$TEST_DNS_PORT" git.kaleschke.info @127.0.0.1 >/tmp/adguard-drill.out 2>&1; then + dns_status="ok" + dns_detail="$(grep -E '^[[:alnum:].-]+\\.[[:space:]]+[0-9]+[[:space:]]+IN[[:space:]]+A[[:space:]]+' /tmp/adguard-drill.out | head -1 || true)" + else + dns_status="failed" + dns_detail="$(tail -20 /tmp/adguard-drill.out | tr '\n' ' ')" + fi +fi + +if [ "$dns_status" = "failed" ]; then + echo "AdGuard DNS smoke failed: $dns_detail" >&2 + docker logs --tail 80 restoretest-adguard >&2 || true + exit 1 +fi + +write_report "$REPORT_FILE" < $REPORT_FILE" diff --git a/ops/restore-tests/run-restore-checks.sh b/ops/restore-tests/run-restore-checks.sh index 30536ed..5c336cb 100755 --- a/ops/restore-tests/run-restore-checks.sh +++ b/ops/restore-tests/run-restore-checks.sh @@ -40,6 +40,12 @@ case "$MODE" in fi exec "$SCRIPT_DIR/authelia-restore-test.sh" ;; + adguard) + if [ "$WHATIF" = "--what-if" ]; then + exec "$SCRIPT_DIR/adguard-restore-test.sh" --what-if + fi + exec "$SCRIPT_DIR/adguard-restore-test.sh" + ;; nextcloud) if [ "$WHATIF" = "--what-if" ]; then exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if @@ -83,7 +89,7 @@ case "$MODE" in exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh" ;; *) - echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|nextcloud|komodo-bootstrap|komodo-mongo-restore|shared-pg-cluster} [--what-if]" >&2 + echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|adguard|nextcloud|komodo-bootstrap|komodo-mongo-restore|traefik|mailarchiver|mealie|shared-pg-cluster} [--what-if]" >&2 exit 1 ;; esac