ops: add redis restore test
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
services:
|
||||
restoretest-redis:
|
||||
image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
|
||||
container_name: restoretest-redis
|
||||
restart: "no"
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- exec redis-server --appendonly yes --requirepass "$$(cat /run/secrets/redis_password)"
|
||||
ports:
|
||||
- "127.0.0.1:16379:6379/tcp"
|
||||
volumes:
|
||||
- /mnt/user/backups/restore-lab/redis/data:/data
|
||||
- /mnt/user/backups/restore-lab/redis/secrets/redis_password.txt:/run/secrets/redis_password:ro
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Redis 8 Restore Smoke Test
|
||||
#
|
||||
# Scope:
|
||||
# - Borg-Extract von /local/appdata/redis und redis_password.txt
|
||||
# - Start einer isolierten Redis-8-Testinstanz auf localhost:16379
|
||||
# - PING, INFO server und DBSIZE ohne Ausgabe des Passworts
|
||||
|
||||
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/redis"
|
||||
REPORT_ROOT="/mnt/user/backups/restore-reports"
|
||||
EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/redis-extract"
|
||||
COMPOSE_FILE="$SCRIPT_DIR/redis-compose.test.yml"
|
||||
REPORT_FILE="$REPORT_ROOT/redis-$(date +%F).md"
|
||||
|
||||
if [ "$WHATIF" -eq 1 ]; then
|
||||
cat <<EOF
|
||||
Redis 8 restore test
|
||||
Mode: WhatIf
|
||||
RestoreRoot: $RESTORE_ROOT
|
||||
Borg sources:
|
||||
- local/appdata/redis
|
||||
- local/appdata/secrets/redis_password.txt
|
||||
Test endpoint: 127.0.0.1:16379
|
||||
Scope: Data restore + isolated Redis boot + PING/INFO/DBSIZE smoke
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_cmd docker
|
||||
require_path "$BORG_PASSPHRASE_FILE_DEFAULT"
|
||||
require_path "$COMPOSE_FILE"
|
||||
|
||||
RESTORE_SUCCESS=0
|
||||
cleanup() {
|
||||
cleanup_compose "$COMPOSE_FILE"
|
||||
if [ "$RESTORE_SUCCESS" -ne 1 ]; then
|
||||
preserve_on_failure "redis" "$RESTORE_ROOT"
|
||||
rm -rf "$EXTRACT_DIR"
|
||||
return
|
||||
fi
|
||||
if [ "$KEEP_DATA" -ne 1 ]; then
|
||||
rm -rf "$RESTORE_ROOT"
|
||||
fi
|
||||
rm -rf "$EXTRACT_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
rm -rf "$EXTRACT_DIR" "$RESTORE_ROOT"
|
||||
mkdir -p "$RESTORE_ROOT/data" "$RESTORE_ROOT/secrets"
|
||||
|
||||
archive="$(latest_archive_name)"
|
||||
repo="$(borg_repo_url)"
|
||||
|
||||
if [ -z "$archive" ] || [ -z "$repo" ]; then
|
||||
echo "Could not resolve Borg repo/archive from borg-ui database" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
borg_extract "/restore/redis-extract" \
|
||||
"local/appdata/redis" \
|
||||
"local/appdata/secrets/redis_password.txt"
|
||||
|
||||
if [ ! -d "$EXTRACT_DIR/local/appdata/redis" ]; then
|
||||
echo "Redis data path missing in Borg archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -s "$EXTRACT_DIR/local/appdata/secrets/redis_password.txt" ]; then
|
||||
echo "Redis password file missing in Borg archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -a "$EXTRACT_DIR/local/appdata/redis/." "$RESTORE_ROOT/data/"
|
||||
cp "$EXTRACT_DIR/local/appdata/secrets/redis_password.txt" "$RESTORE_ROOT/secrets/redis_password.txt"
|
||||
chmod -R a+rwX "$RESTORE_ROOT/data"
|
||||
chmod a+r "$RESTORE_ROOT/secrets/redis_password.txt"
|
||||
|
||||
data_files="$(find "$RESTORE_ROOT/data" -type f | wc -l | tr -d ' ')"
|
||||
data_bytes="$(du -sb "$RESTORE_ROOT/data" | awk '{print $1}')"
|
||||
|
||||
docker compose -f "$COMPOSE_FILE" up -d restoretest-redis >/dev/null
|
||||
|
||||
ping_result=""
|
||||
for _ in $(seq 1 60); do
|
||||
ping_result="$(docker exec restoretest-redis sh -lc \
|
||||
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning PING' 2>/dev/null || true)"
|
||||
if [ "$ping_result" = "PONG" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
if [ "$ping_result" != "PONG" ]; then
|
||||
echo "Redis PING smoke failed: $ping_result" >&2
|
||||
docker logs --tail 80 restoretest-redis >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
redis_version="$(docker exec restoretest-redis sh -lc \
|
||||
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning INFO server | awk -F: "/^redis_version:/ {gsub(/\r/, \"\", \$2); print \$2}"')"
|
||||
dbsize="$(docker exec restoretest-redis sh -lc \
|
||||
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning DBSIZE' | tr -d '\r')"
|
||||
aof_enabled="$(docker exec restoretest-redis sh -lc \
|
||||
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning INFO persistence | awk -F: "/^aof_enabled:/ {gsub(/\r/, \"\", \$2); print \$2}"')"
|
||||
|
||||
case "$redis_version" in
|
||||
8.*) ;;
|
||||
*)
|
||||
echo "Unexpected Redis version: $redis_version" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "${dbsize:-0}" -lt 1 ]; then
|
||||
echo "Unexpected Redis DBSIZE: $dbsize" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
write_report "$REPORT_FILE" <<EOF
|
||||
# Redis 8 Restore Test Report - $(date +%F)
|
||||
|
||||
- Service: \`redis\`
|
||||
- Source repo: \`$repo\`
|
||||
- Archive: \`$archive\`
|
||||
- Restore root: \`$RESTORE_ROOT\`
|
||||
- Test container: \`restoretest-redis\`
|
||||
- Test endpoint: \`127.0.0.1:16379\`
|
||||
- Result: \`SUCCESS\`
|
||||
|
||||
## Checks
|
||||
|
||||
- Borg extract of data path: \`ok\`
|
||||
- Borg extract of secret file: \`ok\`
|
||||
- Restored data files: \`$data_files\`
|
||||
- Restored data bytes: \`$data_bytes\`
|
||||
- PING: \`$ping_result\`
|
||||
- Redis version: \`$redis_version\`
|
||||
- AOF enabled: \`$aof_enabled\`
|
||||
- DBSIZE: \`$dbsize\`
|
||||
|
||||
## Notes
|
||||
|
||||
- Productive Redis port 6379 and productive data path were NOT used.
|
||||
- Test port was bound to localhost only: \`127.0.0.1:16379\`.
|
||||
- Redis password value was used from the restored secret file and was not printed.
|
||||
- Test data was cleaned after success: \`$([ "$KEEP_DATA" -eq 1 ] && echo no || echo yes)\`
|
||||
EOF
|
||||
|
||||
RESTORE_SUCCESS=1
|
||||
echo "Redis restore test ok -> $REPORT_FILE"
|
||||
@@ -46,6 +46,12 @@ case "$MODE" in
|
||||
fi
|
||||
exec "$SCRIPT_DIR/adguard-restore-test.sh"
|
||||
;;
|
||||
redis)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/redis-restore-test.sh" --what-if
|
||||
fi
|
||||
exec "$SCRIPT_DIR/redis-restore-test.sh"
|
||||
;;
|
||||
nextcloud)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if
|
||||
@@ -89,7 +95,7 @@ case "$MODE" in
|
||||
exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh"
|
||||
;;
|
||||
*)
|
||||
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
|
||||
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|adguard|redis|nextcloud|komodo-bootstrap|komodo-mongo-restore|traefik|mailarchiver|mealie|shared-pg-cluster} [--what-if]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user