From a244f2d677eb21cdffdc9a810b8381eb53506091 Mon Sep 17 00:00:00 2001 From: Micha Date: Wed, 3 Jun 2026 11:18:39 +0200 Subject: [PATCH] feat(restore): komodo mongo data restore test Neuer Test: mongorestore von komodo-mongo.archive.gz in eine frische Wegwerf-Mongo. Beweist, dass die Stack-Definitionen und damit die KOMODO_*-Stack-ENV-Werte aus dem Dump rekonstruiert werden koennen (kanonische Quelle laut docs/DISASTER_RECOVERY.md 6.2.1). Machbarkeit vorab verifiziert: Dump 6.0M auf Host vorhanden, mongorestore im mongo:7.0.32-Image verfuegbar, shfs-Write funktioniert. Co-Authored-By: Claude Opus 4.7 --- .../komodo-mongo-restore-test.sh | 159 ++++++++++++++++++ ops/restore-tests/run-restore-checks.sh | 8 +- 2 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 ops/restore-tests/komodo-mongo-restore-test.sh diff --git a/ops/restore-tests/komodo-mongo-restore-test.sh b/ops/restore-tests/komodo-mongo-restore-test.sh new file mode 100644 index 0000000..910caec --- /dev/null +++ b/ops/restore-tests/komodo-mongo-restore-test.sh @@ -0,0 +1,159 @@ +#!/bin/bash +set -euo pipefail + +# Komodo Mongo Daten-Restore Test +# +# Baut auf dem bestehenden Komodo-Bootstrap-Test auf und fuegt hinzu: +# - mongorestore von komodo-mongo.archive.gz in die Test-Mongo +# - Liest danach die stack-Collection, um zu pruefen, dass Komodo- +# Stack-Definitionen wiederhergestellt sind +# +# Das ist der Test, der im DR-Fall beweist, dass die KOMODO_*-Stack- +# ENV-Werte aus dem Mongo-Dump rekonstruiert werden koennen (die +# kanonische Quelle gemaess docs/DISASTER_RECOVERY.md 6.2.1). +# +# Produktive Komodo-Container und produktive Mongo-Datadir werden +# NICHT angefasst. + +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/komodo-mongo-restore" +REPORT_ROOT="/mnt/user/backups/restore-reports" +COMPOSE_FILE="$SCRIPT_DIR/komodo-bootstrap-compose.test.yml" +PROJECT_NAME="restoretest-komodo-mongo" +REPORT_FILE="$REPORT_ROOT/komodo-mongo-restore-$(date +%F).md" +DUMP_HOST_PATH="/mnt/user/backups/borg/dumps/latest/komodo-mongo.archive.gz" + +if [ "$WHATIF" -eq 1 ]; then + cat </dev/null 2>&1 || true + if [ "$RESTORE_SUCCESS" -ne 1 ]; then + preserve_on_failure "komodo-mongo-restore" "$RESTORE_ROOT" + return + fi + if [ "$KEEP_DATA" -ne 1 ]; then + rm -rf "$RESTORE_ROOT" + fi +} +trap cleanup EXIT + +rm -rf "$RESTORE_ROOT" +mkdir -p "$RESTORE_ROOT/mongo" "$RESTORE_ROOT/core" "$RESTORE_ROOT/keys" "$RESTORE_ROOT/periphery" + +# Stufe 1: Nur Test-Mongo starten (kein Core/Periphery noetig fuer Dump-Restore) +docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" up -d \ + restoretest-komodo-mongo >/dev/null + +mongo_ok=0 +for _ in $(seq 1 30); do + s="$(docker inspect restoretest-komodo-mongo --format '{{.State.Health.Status}}' 2>/dev/null || true)" + if [ "$s" = "healthy" ]; then mongo_ok=1; break; fi + sleep 2 +done +if [ "$mongo_ok" -ne 1 ]; then + echo "Test-Mongo never reported healthy" >&2 + docker logs --tail 80 restoretest-komodo-mongo >&2 || true + exit 1 +fi + +# Stufe 2: mongorestore aus dem Host-Dump +# --drop loescht existierende Collections vor dem Restore (frische DB, also harmlos). +# --gzip weil der Dump als .archive.gz erzeugt wurde. +# Auth mit den Wegwerf-Credentials aus dem Test-Compose. +restore_status="ok" +if ! docker exec -i restoretest-komodo-mongo \ + mongorestore --archive --gzip \ + -u komodo -p restoretest-komodo-mongo-pwd --authenticationDatabase admin \ + --drop \ + < "$DUMP_HOST_PATH" 2>/tmp/komodo-mongorestore.err; then + restore_status="failed" + cat /tmp/komodo-mongorestore.err >&2 + exit 1 +fi + +# Stufe 3: Stack-Collection auslesen +# Komodo speichert Stack-Definitionen in der DB "komodo", Collection "Stack" +# (oder "stack" je nach Version). Wir zaehlen Dokumente als Beweis. +stack_count="n/a" +for coll in Stack stack; do + count="$(docker exec restoretest-komodo-mongo mongosh --quiet \ + -u komodo -p restoretest-komodo-mongo-pwd --authenticationDatabase admin \ + --eval "db.getSiblingDB('komodo').getCollection('$coll').countDocuments({})" \ + 2>/dev/null | tr -d '[:space:]' || true)" + if [ -n "$count" ] && [ "$count" != "0" ] && [ "$count" != "n/a" ]; then + stack_count="$count (collection: $coll)" + break + fi +done + +# Alle DBs + Collections auflisten als zusaetzlicher Nachweis +db_list="$(docker exec restoretest-komodo-mongo mongosh --quiet \ + -u komodo -p restoretest-komodo-mongo-pwd --authenticationDatabase admin \ + --eval "db.adminCommand({listDatabases:1}).databases.map(d=>d.name).join(', ')" \ + 2>/dev/null | tr -d '\r' || echo "n/a")" + +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 9cc6292..9b00405 100755 --- a/ops/restore-tests/run-restore-checks.sh +++ b/ops/restore-tests/run-restore-checks.sh @@ -52,8 +52,14 @@ case "$MODE" in fi exec "$SCRIPT_DIR/komodo-bootstrap-test.sh" ;; + komodo-mongo-restore) + if [ "$WHATIF" = "--what-if" ]; then + exec "$SCRIPT_DIR/komodo-mongo-restore-test.sh" --what-if + fi + exec "$SCRIPT_DIR/komodo-mongo-restore-test.sh" + ;; *) - echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|nextcloud|komodo-bootstrap} [--what-if]" >&2 + echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|nextcloud|komodo-bootstrap|komodo-mongo-restore} [--what-if]" >&2 exit 1 ;; esac