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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 <<EOF
|
||||||
|
Komodo Mongo Daten-Restore Test
|
||||||
|
Mode: WhatIf
|
||||||
|
RestoreRoot: $RESTORE_ROOT
|
||||||
|
ReportRoot: $REPORT_ROOT
|
||||||
|
DumpPath: $DUMP_HOST_PATH
|
||||||
|
Planned steps:
|
||||||
|
1. Frische Test-Mongo hochfahren (gleiche Compose wie Bootstrap-Test)
|
||||||
|
2. mongorestore --archive --gzip aus $DUMP_HOST_PATH
|
||||||
|
3. Stack-Collection auslesen (Beweis: Stack-Definitionen sind da)
|
||||||
|
4. Report schreiben
|
||||||
|
5. Cleanup
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_cmd docker
|
||||||
|
require_path "$COMPOSE_FILE"
|
||||||
|
require_path "$DUMP_HOST_PATH"
|
||||||
|
|
||||||
|
RESTORE_SUCCESS=0
|
||||||
|
cleanup() {
|
||||||
|
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" down -v >/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" <<EOF
|
||||||
|
# Komodo Mongo Daten-Restore Test - $(date +%F)
|
||||||
|
|
||||||
|
- Dump: \`$DUMP_HOST_PATH\`
|
||||||
|
- Dump size: \`$(ls -lh "$DUMP_HOST_PATH" | awk '{print $5}')\`
|
||||||
|
- Project: \`$PROJECT_NAME\`
|
||||||
|
- Restore root: \`$RESTORE_ROOT\`
|
||||||
|
- Result: \`SUCCESS\`
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
- Test-Mongo healthy: \`ok\`
|
||||||
|
- mongorestore --archive --gzip: \`$restore_status\`
|
||||||
|
- Databases after restore: \`$db_list\`
|
||||||
|
- Stack documents: \`$stack_count\`
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Dieser Test beweist, dass \`komodo-mongo.archive.gz\` in eine frische
|
||||||
|
Mongo-Instanz eingespielt werden kann und die Stack-Definitionen danach
|
||||||
|
lesbar sind. Im DR-Fall ist das die kanonische Quelle fuer
|
||||||
|
\`KOMODO_*\`-Stack-ENV-Werte (docs/DISASTER_RECOVERY.md 6.2.1).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Produktive Komodo-Container und produktive Mongo-Datadir wurden nicht beruehrt.
|
||||||
|
- Test-Mongo nutzt Wegwerf-Credentials (restoretest-komodo-mongo-pwd).
|
||||||
|
- Kein Komodo-Core gestartet (nicht noetig fuer Dump-Restore-Nachweis).
|
||||||
|
- Test-Daten wurden \`$([ "$KEEP_DATA" -eq 1 ] && echo behalten || echo bereinigt)\`.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
RESTORE_SUCCESS=1
|
||||||
|
echo "Komodo Mongo restore test ok -> $REPORT_FILE"
|
||||||
@@ -52,8 +52,14 @@ case "$MODE" in
|
|||||||
fi
|
fi
|
||||||
exec "$SCRIPT_DIR/komodo-bootstrap-test.sh"
|
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
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user