#!/usr/bin/env bash # DR-тест: проверка ВОССТАНОВИМОСТИ бэкапа PostgreSQL. # # Берёт последний дамп из MinIO (backups/pg/), поднимает ЭФЕМЕРНЫЙ контейнер # postgres:16, восстанавливает в него дамп и проверяет, что ключевые таблицы не # пусты. Прод не трогается — всё в одноразовом контейнере/сети, удаляются на выходе. # # Зачем: бэкапы делаются (cron на 1.38), но восстановление раньше никто не проверял — # «непроверенный бэкап = отсутствие бэкапа». Запускать по cron раз в неделю. # # Требуется docker + доступ до MinIO. Креды берутся из .env (или окружения): # MINIO_ENDPOINT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY, # MINIO_BUCKET_BACKUPS (=backups), POSTGRES_DB, POSTGRES_USER. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" if [ -f "$ROOT/.env" ]; then set -a; . "$ROOT/.env"; set +a; fi : "${MINIO_ENDPOINT:?нужен MINIO_ENDPOINT}" : "${MINIO_ACCESS_KEY:?нужен MINIO_ACCESS_KEY}" : "${MINIO_SECRET_KEY:?нужен MINIO_SECRET_KEY}" BUCKET="${MINIO_BUCKET_BACKUPS:-backups}" PREFIX="${PG_BACKUP_PREFIX:-pg/}" PGDB="${POSTGRES_DB:-antiplagiator}" PGUSER="${POSTGRES_USER:-antiplagiator}" SUFFIX="$$-$RANDOM" PG="drtest-pg-$SUFFIX" WORK="$(mktemp -d)" MC_HOST="http://${MINIO_ACCESS_KEY}:${MINIO_SECRET_KEY}@${MINIO_ENDPOINT}" cleanup() { docker rm -f "$PG" >/dev/null 2>&1 || true; rm -rf "$WORK"; } trap cleanup EXIT echo "▶ Поиск последнего дампа в minio://$BUCKET/$PREFIX" LATEST="$(docker run --rm -e MC_HOST_dr="$MC_HOST" minio/mc:latest \ sh -c "mc ls --recursive dr/$BUCKET/$PREFIX | awk '{print \$NF}' | grep -E '\.(sql\.)?gz$' | sort | tail -1")" [ -n "$LATEST" ] || { echo "❌ дампы не найдены"; exit 1; } FNAME="$(basename "$LATEST")" echo " последний: $FNAME" echo "▶ Скачивание дампа" docker run --rm -e MC_HOST_dr="$MC_HOST" -v "$WORK":/w minio/mc:latest \ sh -c "mc cp 'dr/$BUCKET/$PREFIX$FNAME' /w/dump.gz" echo "▶ Эфемерный postgres:16" docker run -d --name "$PG" \ -e POSTGRES_PASSWORD=drtest -e POSTGRES_DB="$PGDB" -e POSTGRES_USER="$PGUSER" \ postgres:16 >/dev/null for _ in $(seq 1 30); do docker exec "$PG" pg_isready -U "$PGUSER" -d "$PGDB" >/dev/null 2>&1 && break sleep 1 done echo "▶ Восстановление" gunzip -c "$WORK/dump.gz" | docker exec -i "$PG" psql -v ON_ERROR_STOP=0 -U "$PGUSER" -d "$PGDB" >/dev/null 2>&1 echo "▶ Проверка ключевых таблиц" rc=0 for tbl in users documents tasks; do n="$(docker exec "$PG" psql -U "$PGUSER" -d "$PGDB" -tAc "SELECT count(*) FROM $tbl" 2>/dev/null | tr -d '[:space:]' || echo ERR)" echo " $tbl: ${n:-ERR}" { [ -z "$n" ] || [ "$n" = "ERR" ]; } && rc=1 done if [ "$rc" -eq 0 ]; then echo "✅ Бэкап восстанавливается, ключевые таблицы на месте ($FNAME)" else echo "❌ Восстановление не прошло проверку — разобраться СРОЧНО" exit 1 fi