divan/deploy/restore.sh
Anas Rashid 06f56012ec Backups, restore and rollback runbook (#10, #12)
- deploy/backup.sh / deploy/restore.sh (compose service or local container; side-by-side test restore)
- restore: chown copied backup for sqlservr, fail loudly on unreadable file list
- README: update, backup, test-restore and rollback steps

Validated locally: backup 194 MB, restored into a test DB with identical counts, test DB dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +02:00

31 lines
1.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Restore a backup made by deploy/backup.sh.
# deploy/restore.sh backups/diwan-<stamp>.bak (replaces database "diwan"; stop the api first)
# TARGET_DB=diwan_restoretest deploy/restore.sh <file> (restore side by side, e.g. to test a backup)
# DB_CONTAINER as in backup.sh. Needs MSSQL_SA_PASSWORD.
set -euo pipefail
cd "$(dirname "$0")/.."
[ -f .env ] && set -a && . ./.env && set +a
: "${MSSQL_SA_PASSWORD:?set MSSQL_SA_PASSWORD}"
BAK=${1:?usage: deploy/restore.sh <backup file>}
TARGET=${TARGET_DB:-diwan}
C=${DB_CONTAINER:-$(docker compose ps -q db)}
SQL() { docker exec "$C" /opt/mssql-tools18/bin/sqlcmd -C -b -S localhost -U sa -P "$MSSQL_SA_PASSWORD" "$@"; }
docker exec "$C" mkdir -p /var/opt/mssql/backup
docker cp "$BAK" "$C:/var/opt/mssql/backup/restore.bak"
docker exec -u 0 "$C" chown mssql /var/opt/mssql/backup/restore.bak # docker cp keeps the host uid; sqlservr runs as mssql
# logical file names inside the backup, moved to files named after the target database
FILES=$(SQL -h -1 -W -s '|' -Q "SET NOCOUNT ON; RESTORE FILELISTONLY FROM DISK = N'/var/opt/mssql/backup/restore.bak'" | awk -F'|' 'NF>2 {print $1"|"$3}')
[ -n "$FILES" ] || { echo "could not read the backup's file list" >&2; exit 1; }
MOVE=""
while IFS='|' read -r logical type; do
[ -z "$logical" ] && continue
ext=mdf; [ "$type" = L ] && ext=ldf
MOVE="$MOVE, MOVE N'$logical' TO N'/var/opt/mssql/data/${TARGET}_${logical}.$ext'"
done <<< "$FILES"
SQL -Q "IF DB_ID(N'$TARGET') IS NOT NULL ALTER DATABASE [$TARGET] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE [$TARGET] FROM DISK = N'/var/opt/mssql/backup/restore.bak' WITH REPLACE, CHECKSUM$MOVE;
ALTER DATABASE [$TARGET] SET MULTI_USER;"
docker exec "$C" rm -f /var/opt/mssql/backup/restore.bak
echo "restored $BAK into [$TARGET]"