Files
orbit/scripts/test-backup-restore.sh

107 lines
4.5 KiB
Bash
Executable File

#!/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"