mirror of
https://github.com/lobaro/restic-backup-docker.git
synced 2026-11-13 23:45:24 +00:00
Adds structure, compat and feature-gated container test suites (test/run.sh), runs them in CI, and gates image publishing on a green test run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
173 lines
6.7 KiB
Bash
173 lines
6.7 KiB
Bash
#!/usr/bin/env bash
|
|
# Feature suite.
|
|
#
|
|
# Each block is guarded by a detection check against the built image. On master
|
|
# every block reports SKIP. As soon as the corresponding pull request is rebased
|
|
# onto master, the feature is detected and its tests start running - the PR
|
|
# author does not have to write test code for the basics.
|
|
#
|
|
# After merging a feature, add its gate name to test/required-features.txt.
|
|
# From then on a missing feature is a failure instead of a skip.
|
|
|
|
set -u
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=test/lib.sh
|
|
. "${SCRIPT_DIR}/lib.sh"
|
|
|
|
trap stop_container EXIT
|
|
|
|
echo "=== feature: stream backup (RESTIC_STREAM) ==="
|
|
# Pull request #109 - back up the output of a command via `restic backup --stdin`.
|
|
|
|
if require_feature stream "PR #109"; then
|
|
start_container stream \
|
|
-e RESTIC_STREAM=1 \
|
|
-e "RESTIC_STREAM_CMD=echo hello-stream" \
|
|
-e RESTIC_STREAM_FILENAME=stream.txt
|
|
if wait_for_start; then
|
|
stream_log="$(in_container /bin/backup 2>&1)"
|
|
assert_contains "$stream_log" "Backup Successful" \
|
|
"stream backup succeeds"
|
|
assert_eq "1" "$(snapshot_count)" \
|
|
"stream backup creates exactly one snapshot"
|
|
in_container restic restore latest --target /tmp/restore >/dev/null 2>&1
|
|
restored="$(in_container sh -c 'cat /tmp/restore/stream.txt 2>/dev/null' 2>&1)"
|
|
assert_eq "hello-stream" "$(echo "$restored" | tr -d '\r')" \
|
|
"stream content is stored under RESTIC_STREAM_FILENAME"
|
|
else
|
|
fail "stream backup container starts"
|
|
fi
|
|
stop_container
|
|
|
|
# Blocker 1 of PR #109: the stream branch must honour RESTIC_JOB_ARGS, the
|
|
# same way the file backup does. --host is used because it is visible in
|
|
# the snapshot metadata of a stdin backup.
|
|
start_container stream-jobargs \
|
|
-e RESTIC_STREAM=1 \
|
|
-e "RESTIC_STREAM_CMD=echo hello-stream" \
|
|
-e RESTIC_STREAM_FILENAME=stream.txt \
|
|
-e "RESTIC_JOB_ARGS=--host=jobargshost"
|
|
if wait_for_start; then
|
|
in_container /bin/backup >/dev/null 2>&1
|
|
# Match on the hostname field itself: restic's own --host filter only
|
|
# returns snapshots whose hostname equals the value, so the string
|
|
# cannot be satisfied by "jobargshost" appearing in some other field.
|
|
# Both directions are checked so the filter is known to discriminate.
|
|
assert_eq "1" "$(in_container sh -c \
|
|
'restic snapshots --host jobargshost --json 2>/dev/null | grep -o "\"short_id\"" | wc -l' | tr -d ' \r')" \
|
|
"RESTIC_JOB_ARGS sets the host of the stream backup snapshot"
|
|
assert_eq "0" "$(in_container sh -c \
|
|
'restic snapshots --host some-other-host --json 2>/dev/null | grep -o "\"short_id\"" | wc -l' | tr -d ' \r')" \
|
|
"the host filter does not match an unrelated hostname"
|
|
else
|
|
fail "stream RESTIC_JOB_ARGS container starts"
|
|
fi
|
|
stop_container
|
|
|
|
# A half-configured stream must not look like a successful backup.
|
|
start_container stream-misconfigured -e RESTIC_STREAM=1
|
|
if wait_for_start; then
|
|
misconfig_log="$(in_container /bin/backup 2>&1)"
|
|
assert_not_contains "$misconfig_log" "Backup Successful" \
|
|
"incomplete stream configuration does not report success"
|
|
assert_eq "0" "$(snapshot_count)" \
|
|
"incomplete stream configuration creates no snapshot"
|
|
else
|
|
fail "stream misconfiguration container starts"
|
|
fi
|
|
stop_container
|
|
|
|
# The classic path must keep working while the feature is available.
|
|
start_container stream-disabled
|
|
if wait_for_start; then
|
|
write_file /data/hello.txt "backup me"
|
|
in_container /bin/backup >/dev/null 2>&1
|
|
assert_eq "1" "$(snapshot_count)" \
|
|
"file backup still works while the stream feature is present"
|
|
else
|
|
fail "stream-disabled container starts"
|
|
fi
|
|
stop_container
|
|
fi
|
|
|
|
echo
|
|
echo "=== feature: docker cli ==="
|
|
# Pull request #99 - talk to the host docker daemon from hooks.
|
|
|
|
if require_feature docker-cli "PR #99"; then
|
|
docker_version="$(docker run --rm --entrypoint docker "$IMAGE" --version 2>&1)"
|
|
assert_contains "$docker_version" "Docker version" \
|
|
"docker cli is usable inside the image"
|
|
fi
|
|
|
|
echo
|
|
echo "=== feature: bash ==="
|
|
# Pull request #99 - allow bash syntax in pre/post hooks.
|
|
|
|
if require_feature bash "PR #99"; then
|
|
start_container bash-hooks
|
|
if wait_for_start; then
|
|
write_file /hooks/pre-backup.sh '#!/bin/bash
|
|
arr=(one two)
|
|
if [[ "${arr[1]}" == "two" ]]; then echo "BASH-HOOK-RAN"; fi
|
|
'
|
|
in_container chmod +x /hooks/pre-backup.sh
|
|
write_file /data/hello.txt "backup me"
|
|
bash_log="$(in_container /bin/backup 2>&1)"
|
|
assert_contains "$bash_log" "BASH-HOOK-RAN" \
|
|
"a hook with a bash shebang and bash syntax runs"
|
|
else
|
|
fail "bash hook container starts"
|
|
fi
|
|
stop_container
|
|
fi
|
|
|
|
echo
|
|
echo "=== feature: msmtp mail relay ==="
|
|
# Pull request #84 - send notification mails through msmtp.
|
|
|
|
if require_feature msmtp "PR #84"; then
|
|
sendmail_target="$(docker run --rm --entrypoint sh "$IMAGE" -c \
|
|
'readlink -f /usr/sbin/sendmail' 2>&1 | tr -d '\r')"
|
|
assert_contains "$sendmail_target" "msmtp" \
|
|
"sendmail is provided by msmtp"
|
|
|
|
# The config has to be in place when the container starts, entry.sh copies
|
|
# it. Re-running entry.sh in a live container would start a second crond.
|
|
start_container_with_file msmtp-config /config/msmtprc 'defaults
|
|
account default
|
|
host localhost
|
|
'
|
|
if wait_for_start; then
|
|
if in_container test -f /etc/msmtprc; then
|
|
pass "msmtp config from /config is copied to /etc/msmtprc"
|
|
mode="$(in_container stat -c '%a' /etc/msmtprc 2>&1 | tr -d '\r')"
|
|
assert_eq "600" "$mode" \
|
|
"copied msmtp config is not world readable"
|
|
else
|
|
fail "msmtp config from /config is copied to /etc/msmtprc" "file is missing"
|
|
fi
|
|
else
|
|
fail "msmtp config container starts"
|
|
fi
|
|
stop_container
|
|
|
|
# Users who configured mail before msmtp existed must not lose their backups.
|
|
start_container msmtp-missing-config \
|
|
-e "MAILX_ARGS=-r from@example.com -s subject to@example.com"
|
|
if wait_for_start; then
|
|
write_file /data/hello.txt "backup me"
|
|
mail_log="$(in_container /bin/backup 2>&1)"
|
|
assert_contains "$mail_log" "Backup Successful" \
|
|
"backup succeeds with MAILX_ARGS set but no msmtp config"
|
|
assert_eq "1" "$(snapshot_count)" \
|
|
"snapshot is created even when the mail relay is unconfigured"
|
|
else
|
|
fail "msmtp missing-config container starts"
|
|
fi
|
|
stop_container
|
|
fi
|
|
|
|
summary "features"
|