krona-staff/scripts/test-backup.sh

137 lines
4.2 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
repo_root=$(cd -- "$(dirname -- "$0")/.." && pwd)
backup_script="$repo_root/deploy/pilot/backup.sh"
test_parent=$(cd -- /tmp && pwd -P)
test_root=$(mktemp -d "$test_parent/krona-backup-test.XXXXXX")
test_root=$(cd -- "$test_root" && pwd -P)
is_test_root() {
[ "$(dirname -- "$1")" = "$test_parent" ] || return 1
case "$(basename -- "$1")" in krona-backup-test.*) return 0 ;; *) return 1 ;; esac
}
if ! is_test_root "$test_root"; then
printf 'unexpected temporary test directory: %s\n' "$test_root" >&2
exit 1
fi
mock_bin="$test_root/bin"
mkdir -p "$mock_bin"
cleanup() {
resolved_test_root=$(cd -- "$test_root" && pwd -P) || return 1
if ! is_test_root "$resolved_test_root"; then
printf 'refusing to remove unexpected test directory: %s\n' "$resolved_test_root" >&2
return 1
fi
rm -rf -- "$resolved_test_root"
}
trap cleanup EXIT
cat > "$mock_bin/docker" <<'MOCK_DOCKER'
#!/bin/sh
set -eu
if [ "${MOCK_DOCKER_FAIL:-0}" = 1 ]; then
printf 'synthetic partial dump\n'
exit 42
fi
printf 'synthetic PostgreSQL dump\n'
MOCK_DOCKER
cat > "$mock_bin/scp" <<'MOCK_SCP'
#!/bin/sh
set -eu
printf '%s\n' "$@" > "$MOCK_SCP_LOG"
if [ "${MOCK_SCP_FAIL:-0}" = 1 ]; then
exit 43
fi
MOCK_SCP
cat > "$mock_bin/openssl" <<'MOCK_OPENSSL'
#!/bin/sh
set -eu
if [ "${MOCK_OPENSSL_CMS_FAIL:-0}" = 1 ] && [ "${1:-}" = cms ] && [ "${2:-}" = -encrypt ]; then
exit 44
fi
exec /usr/bin/openssl "$@"
MOCK_OPENSSL
chmod +x "$mock_bin/docker" "$mock_bin/scp" "$mock_bin/openssl"
cert="$test_root/recipient.crt"
key="$test_root/recipient.key"
/usr/bin/openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 1 \
-keyout "$key" -out "$cert" -subj '/CN=backup-test-recipient' >/dev/null 2>&1
export PATH="$mock_bin:$PATH"
export BACKUP_RECIPIENT_CERT="$cert"
export BACKUP_REMOTE='backup@example.invalid:/backup/krona-staff/'
export MOCK_SCP_LOG="$test_root/scp-args.log"
assert_no_plain_dump() {
backup_dir=$1
if find "$backup_dir" -type f ! -name '*.dump.cms' ! -name '*.dump.cms.sha256' | grep -q .; then
printf 'unexpected non-encrypted backup artifact in %s\n' "$backup_dir" >&2
exit 1
fi
}
run_backup() {
backup_dir=$1
shift
mkdir -p "$backup_dir"
if env BACKUP_DIRECTORY="$backup_dir" "$@" sh "$backup_script"; then
return 0
else
return $?
fi
}
sh -n "$backup_script"
success_dir="$test_root/success"
run_backup "$success_dir" env
encrypted=$(find "$success_dir" -maxdepth 1 -type f -name '*.dump.cms' -print -quit)
test -n "$encrypted"
test -s "$encrypted.sha256"
(cd "$success_dir" && sha256sum -c "$(basename -- "$encrypted").sha256") >/dev/null
cmp <(printf 'synthetic PostgreSQL dump\n') \
<(/usr/bin/openssl cms -decrypt -binary -inform DER -in "$encrypted" -recip "$cert" -inkey "$key")
assert_no_plain_dump "$success_dir"
test "$(wc -l < "$MOCK_SCP_LOG")" -eq 4
pull_dir="$test_root/external-pull"
pull_output=$(run_backup "$pull_dir" env BACKUP_TRANSFER_MODE=external-pull BACKUP_REMOTE=)
test "$(find "$pull_dir" -maxdepth 1 -type f | wc -l)" -eq 2
printf '%s\n' "$pull_output" | grep -q '^off_host_copy=pending_pull$'
assert_no_plain_dump "$pull_dir"
test "$(wc -l < "$MOCK_SCP_LOG")" -eq 4
assert_failure_cleans_scratch() {
backup_dir=$1
if find "$backup_dir" -mindepth 1 -print -quit | grep -q .; then
printf 'unexpected artifact after failed backup in %s\n' "$backup_dir" >&2
exit 1
fi
}
dump_fail_dir="$test_root/pg-dump-failure"
if run_backup "$dump_fail_dir" env MOCK_DOCKER_FAIL=1; then
printf 'backup succeeded after mocked pg_dump failure\n' >&2
exit 1
fi
assert_failure_cleans_scratch "$dump_fail_dir"
openssl_fail_dir="$test_root/openssl-failure"
if run_backup "$openssl_fail_dir" env MOCK_OPENSSL_CMS_FAIL=1; then
printf 'backup succeeded after mocked CMS encryption failure\n' >&2
exit 1
fi
assert_failure_cleans_scratch "$openssl_fail_dir"
scp_fail_dir="$test_root/scp-failure"
if run_backup "$scp_fail_dir" env MOCK_SCP_FAIL=1; then
printf 'backup succeeded after mocked scp failure\n' >&2
exit 1
fi
assert_no_plain_dump "$scp_fail_dir"
test "$(find "$scp_fail_dir" -maxdepth 1 -type f | wc -l)" -eq 2
printf 'backup tests passed: syntax, CMS round-trip, pg_dump/OpenSSL/scp failures, no plaintext dump on disk\n'