137 lines
4.2 KiB
Bash
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'
|