test: add isolated backup restore rehearsal
This commit is contained in:
106
scripts/test-backup-restore.sh
Executable file
106
scripts/test-backup-restore.sh
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/bin/sh
|
||||
# Rehearses a logical MySQL backup into a separate disposable database.
|
||||
# It never points at an existing database and does not modify a production target.
|
||||
set -eu
|
||||
|
||||
TASK_DOCKER=${DOCKER:-docker}
|
||||
TASK_MIGRATE=${ORBIT_MIGRATE_BIN:-bin/orbit-migrate}
|
||||
TASK_ADMIN=${ORBIT_ADMIN_BIN:-bin/orbit-admin}
|
||||
TASK_SOURCE_NAME="orbit-backup-source-$$"
|
||||
TASK_RESTORE_NAME="orbit-backup-restore-$$"
|
||||
TASK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/orbit-backup.XXXXXX")
|
||||
TASK_SOURCE_PID=
|
||||
TASK_RESTORE_PID=
|
||||
|
||||
cleanup() {
|
||||
"$TASK_DOCKER" rm -f "$TASK_SOURCE_NAME" "$TASK_RESTORE_NAME" >/dev/null 2>&1 || true
|
||||
for task_artifact in "$TASK_DIR"/*; do
|
||||
if [ -f "$task_artifact" ]; then unlink "$task_artifact"; fi
|
||||
done
|
||||
rmdir "$TASK_DIR" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
if [ ! -x "$TASK_MIGRATE" ] || [ ! -x "$TASK_ADMIN" ]; then
|
||||
printf '%s\n' 'orbit-migrate and orbit-admin binaries are required; build them before this rehearsal.' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
umask 077
|
||||
TASK_SOURCE_PASSWORD=$(openssl rand -hex 24)
|
||||
TASK_RESTORE_PASSWORD=$(openssl rand -hex 24)
|
||||
|
||||
start_mysql() {
|
||||
container=$1
|
||||
database=$2
|
||||
password=$3
|
||||
"$TASK_DOCKER" run -d --name "$container" \
|
||||
-e MYSQL_ROOT_PASSWORD="$password" \
|
||||
-e MYSQL_DATABASE="$database" \
|
||||
-p 127.0.0.1::3306 "$ORBIT_TEST_MYSQL_IMAGE" >/dev/null
|
||||
ready=0
|
||||
for attempt in $(seq 1 60); do
|
||||
if "$TASK_DOCKER" exec -e MYSQL_PWD="$password" "$container" \
|
||||
mysql -uroot --protocol=tcp -e 'SELECT 1' >/dev/null 2>&1; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [ "$ready" -ne 1 ]; then
|
||||
printf 'MySQL container did not become ready: %s\n' "$container" >&2
|
||||
"$TASK_DOCKER" logs --tail 30 "$container" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
query() {
|
||||
container=$1
|
||||
password=$2
|
||||
database=$3
|
||||
sql=$4
|
||||
"$TASK_DOCKER" exec -e MYSQL_PWD="$password" "$container" \
|
||||
mysql -uroot --protocol=tcp --batch --skip-column-names "$database" -e "$sql"
|
||||
}
|
||||
|
||||
ORBIT_TEST_MYSQL_IMAGE=${ORBIT_TEST_MYSQL_IMAGE:-mysql:8.4}
|
||||
start_mysql "$TASK_SOURCE_NAME" orbit_source "$TASK_SOURCE_PASSWORD"
|
||||
start_mysql "$TASK_RESTORE_NAME" orbit_restore "$TASK_RESTORE_PASSWORD"
|
||||
|
||||
TASK_SOURCE_PORT=$("$TASK_DOCKER" port "$TASK_SOURCE_NAME" 3306/tcp | sed 's/.*://')
|
||||
TASK_RESTORE_PORT=$("$TASK_DOCKER" port "$TASK_RESTORE_NAME" 3306/tcp | sed 's/.*://')
|
||||
TASK_SOURCE_DSN="root:$TASK_SOURCE_PASSWORD@tcp(127.0.0.1:$TASK_SOURCE_PORT)/orbit_source"
|
||||
TASK_RESTORE_DSN="root:$TASK_RESTORE_PASSWORD@tcp(127.0.0.1:$TASK_RESTORE_PORT)/orbit_restore"
|
||||
|
||||
ORBIT_MYSQL_DSN="$TASK_SOURCE_DSN" "$TASK_MIGRATE" up >/dev/null
|
||||
ORBIT_MYSQL_DSN="$TASK_SOURCE_DSN" "$TASK_ADMIN" bootstrap \
|
||||
--application backup-rehearsal --namespace default --environment test \
|
||||
--queue restore-check --output "$TASK_DIR/credentials.json" >/dev/null
|
||||
|
||||
TASK_SOURCE_COUNTS=$(query "$TASK_SOURCE_NAME" "$TASK_SOURCE_PASSWORD" orbit_source \
|
||||
"SELECT CONCAT((SELECT COUNT(*) FROM orbit_applications),',',(SELECT COUNT(*) FROM orbit_namespaces),',',(SELECT COUNT(*) FROM orbit_credentials),',',(SELECT COUNT(*) FROM pulse_queues),',',(SELECT COUNT(*) FROM orbit_schema_migrations));")
|
||||
"$TASK_DOCKER" exec -e MYSQL_PWD="$TASK_SOURCE_PASSWORD" "$TASK_SOURCE_NAME" \
|
||||
mysqldump -uroot --protocol=tcp --single-transaction --routines --triggers --events \
|
||||
--hex-blob --no-tablespaces orbit_source > "$TASK_DIR/orbit.sql"
|
||||
|
||||
|
||||
"$TASK_DOCKER" exec -i -e MYSQL_PWD="$TASK_RESTORE_PASSWORD" "$TASK_RESTORE_NAME" \
|
||||
mysql -uroot --protocol=tcp orbit_restore < "$TASK_DIR/orbit.sql"
|
||||
|
||||
TASK_VERIFY=$(ORBIT_MYSQL_DSN="$TASK_RESTORE_DSN" "$TASK_ADMIN" verify)
|
||||
TASK_RESTORE_COUNTS=$(query "$TASK_RESTORE_NAME" "$TASK_RESTORE_PASSWORD" orbit_restore \
|
||||
"SELECT CONCAT((SELECT COUNT(*) FROM orbit_applications),',',(SELECT COUNT(*) FROM orbit_namespaces),',',(SELECT COUNT(*) FROM orbit_credentials),',',(SELECT COUNT(*) FROM pulse_queues),',',(SELECT COUNT(*) FROM orbit_schema_migrations));")
|
||||
TASK_FOREIGN_KEYS=$(query "$TASK_RESTORE_NAME" "$TASK_RESTORE_PASSWORD" information_schema \
|
||||
"SELECT COUNT(*) FROM REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='orbit_restore' AND LEFT(TABLE_NAME,6) IN ('orbit_','pulse_','weave_');")
|
||||
|
||||
if [ "$TASK_SOURCE_COUNTS" != "$TASK_RESTORE_COUNTS" ]; then
|
||||
printf 'restored core counts differ: source=%s restore=%s\n' "$TASK_SOURCE_COUNTS" "$TASK_RESTORE_COUNTS" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$TASK_FOREIGN_KEYS" != 0 ]; then
|
||||
printf 'restored schema has %s Orbit foreign keys\n' "$TASK_FOREIGN_KEYS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '{"source_core_counts":"%s","restore_core_counts":"%s","restore_foreign_key_count":%s,"verify":%s}\n' \
|
||||
"$TASK_SOURCE_COUNTS" "$TASK_RESTORE_COUNTS" "$TASK_FOREIGN_KEYS" "$TASK_VERIFY"
|
||||
Reference in New Issue
Block a user