diff --git a/cmd/backup/main.go b/cmd/backup/main.go index d1c2b21..54c516b 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -88,8 +88,6 @@ func main() { s.must(s.encryptBackup()) s.must(s.copyBackup()) s.must(s.pruneOldBackups()) - s.stats.EndTime = time.Now() - s.stats.TookTime = s.stats.EndTime.Sub(s.stats.EndTime) } // script holds all the stateful information required to orchestrate a @@ -191,8 +189,6 @@ type Config struct { WebdavPassword string `split_words:"true"` } -var msgBackupFailed = "backup run failed" - // newScript creates all resources needed for the script to perform actions against // remote resources like the Docker engine or remote storage locations. All // reading from env vars or other configuration sources is expected to happen @@ -214,6 +210,12 @@ func newScript() (*script, error) { }, } + s.registerHook(hookLevelPlumbing, func(error) error { + s.stats.EndTime = time.Now() + s.stats.TookTime = s.stats.EndTime.Sub(s.stats.EndTime) + return nil + }) + if err := envconfig.Process("", s.c); err != nil { return nil, fmt.Errorf("newScript: failed to process configuration values: %w", err) } diff --git a/test/cli/run.sh b/test/cli/run.sh index 74c0696..269ef60 100755 --- a/test/cli/run.sh +++ b/test/cli/run.sh @@ -7,6 +7,8 @@ cd $(dirname $0) docker network create test_network docker volume create backup_data docker volume create app_data +# This volume is created to test whether empty directories are handled +# correctly. It is not supposed to hold any data. docker volume create empty_data docker run -d \ @@ -48,8 +50,10 @@ docker run --rm -it \ -v backup_data:/data alpine \ ash -c 'tar -xvf /data/backup/test.tar.gz && test -f /backup/app_data/offen.db && test -d /backup/empty_data' -echo "[TEST:PASS] Found relevant files in untared backup." +echo "[TEST:PASS] Found relevant files in untared remote backup." +# This test does not stop containers during backup. This is happening on +# purpose in order to cover this setup as well. if [ "$(docker ps -q | wc -l)" != "2" ]; then echo "[TEST:FAIL] Expected all containers to be running post backup, instead seen:" docker ps diff --git a/test/compose/run.sh b/test/compose/run.sh index 0d6a226..c1c4ed4 100755 --- a/test/compose/run.sh +++ b/test/compose/run.sh @@ -9,9 +9,20 @@ mkdir -p local docker-compose up -d sleep 5 +# A symlink for a known file in the volume is created so the test can check +# whether symlinks are preserved on backup. docker-compose exec offen ln -s /var/opt/offen/offen.db /var/opt/offen/db.link docker-compose exec backup backup +sleep 5 +if [ "$(docker-compose ps -q | wc -l)" != "4" ]; then + echo "[TEST:FAIL] Expected all containers to be running post backup, instead seen:" + docker-compose ps + exit 1 +fi +echo "[TEST:PASS] All containers running post backup." + + docker run --rm -it \ -v compose_minio_backup_data:/minio_data \ -v compose_webdav_backup_data:/webdav_data alpine \ @@ -19,23 +30,17 @@ docker run --rm -it \ echo 1234secret | gpg -d --pinentry-mode loopback --passphrase-fd 0 --yes /minio_data/backup/test-hostnametoken.tar.gz.gpg > /tmp/test-hostnametoken.tar.gz && tar -xf /tmp/test-hostnametoken.tar.gz -C /tmp && test -f /tmp/backup/app_data/offen.db && \ echo 1234secret | gpg -d --pinentry-mode loopback --passphrase-fd 0 --yes /webdav_data/data/my/new/path/test-hostnametoken.tar.gz.gpg > /tmp/test-hostnametoken.tar.gz && tar -xf /tmp/test-hostnametoken.tar.gz -C /tmp && test -f /tmp/backup/app_data/offen.db' -echo "[TEST:PASS] Found relevant files in untared remote backups." +echo "[TEST:PASS] Found relevant files in decrypted and untared remote backups." -test -L ./local/test-hostnametoken.latest.tar.gz.gpg echo 1234secret | gpg -d --yes --passphrase-fd 0 ./local/test-hostnametoken.tar.gz.gpg > ./local/decrypted.tar.gz tar -xf ./local/decrypted.tar.gz -C /tmp && test -f /tmp/backup/app_data/offen.db rm ./local/decrypted.tar.gz test -L /tmp/backup/app_data/db.link -echo "[TEST:PASS] Found relevant files in untared local backup." +echo "[TEST:PASS] Found relevant files in decrypted and untared local backup." -if [ "$(docker-compose ps -q | wc -l)" != "4" ]; then - echo "[TEST:FAIL] Expected all containers to be running post backup, instead seen:" - docker-compose ps - exit 1 -fi - -echo "[TEST:PASS] All containers running post backup." +test -L ./local/test-hostnametoken.latest.tar.gz.gpg +echo "[TEST:PASS] Found symlink to latest version in local backup." # The second part of this test checks if backups get deleted when the retention # is set to 0 days (which it should not as it would mean all backups get deleted) @@ -56,8 +61,8 @@ echo "[TEST:PASS] Remote backups have not been deleted." if [ "$(find ./local -type f | wc -l)" != "1" ]; then echo "[TEST:FAIL] Backups should not have been deleted, instead seen:" find ./local -type f + exit 1 fi - echo "[TEST:PASS] Local backups have not been deleted." docker-compose down --volumes diff --git a/test/notifications/docker-compose.yml b/test/notifications/docker-compose.yml index 0423c72..8b05484 100644 --- a/test/notifications/docker-compose.yml +++ b/test/notifications/docker-compose.yml @@ -9,7 +9,7 @@ services: BACKUP_CRON_EXPRESSION: 0 0 5 31 2 ? BACKUP_PRUNING_PREFIX: test NOTIFICATION_LEVEL: info - NOTIFICATION_URLS: gotify://gotify/${GOTIFY_TOKEN}?disableTLS=true + NOTIFICATION_URLS: ${NOTIFICATION_URLS} volumes: - ./local:/archive - app_data:/backup/app_data:ro diff --git a/test/notifications/run.sh b/test/notifications/run.sh index 35ba183..f44c607 100755 --- a/test/notifications/run.sh +++ b/test/notifications/run.sh @@ -10,49 +10,43 @@ docker-compose up -d sleep 5 GOTIFY_TOKEN=$(curl -sSLX POST -H 'Content-Type: application/json' -d '{"name":"test"}' http://admin:custom@localhost:8080/application | jq -r '.token') - -docker-compose down - -GOTIFY_TOKEN=$GOTIFY_TOKEN docker-compose up -d - echo "[TEST:INFO] Set up Gotify application using token $GOTIFY_TOKEN" docker-compose exec backup backup -tar -xf ./local/test.tar.gz -C /tmp && test -f /tmp/backup/app_data/offen.db -echo "[TEST:PASS] Found relevant files in untared local backup." - -if [ "$(docker-compose ps -q | wc -l)" != "3" ]; then - echo "[TEST:FAIL] Expected all containers to be running post backup, instead seen:" - docker-compose ps +NUM_MESSAGES=$(curl -sSL http://admin:custom@localhost:8080/message | jq -r '.messages | length') +if [ "$NUM_MESSAGES" != 0 ]; then + echo "[TEST:FAIL] Expected no notifications to be sent when not configured" exit 1 fi +echo "[TEST:PASS] No notifications were sent when not configured." -echo "[TEST:PASS] All containers running post backup." +docker-compose down -MESSAGE=$(curl -sSL http://admin:custom@localhost:8080/message | jq -r '.messages[0]') +NOTIFICATION_URLS="gotify://gotify/${GOTIFY_TOKEN}?disableTLS=true" docker-compose up -d +docker-compose exec backup backup -case "$MESSAGE" in - *"Successful test run, yay!"*) - echo "[TEST:PASS] Custom notification title was used" - ;; - *) - echo "[TEST:FAIL] Expected custom title to be used in notification, instead seen:" - echo $MESSAGE +NUM_MESSAGES=$(curl -sSL http://admin:custom@localhost:8080/message | jq -r '.messages | length') +if [ "$NUM_MESSAGES" != 1 ]; then + echo "[TEST:FAIL] Expected one notifications to be sent when configured" exit 1 - ;; -esac +fi +echo "[TEST:PASS] Correct number of notifications were sent when configured." -case "$MESSAGE" in - *"Backing up /tmp/test.tar.gz succeeded."*) - echo "[TEST:PASS] Custom notification body was used" - ;; - *) - echo "[TEST:FAIL] Expected custom body to be used in notification, instead seen:" - echo $MESSAGE +MESSAGE_TITLE=$(curl -sSL http://admin:custom@localhost:8080/message | jq -r '.messages[0].title') +MESSAGE_BODY=$(curl -sSL http://admin:custom@localhost:8080/message | jq -r '.messages[0].message') + +if [ "$MESSAGE_TITLE" != "Successful test run, yay!" ]; then + echo "[TEST:FAIL] Unexpected notification title $MESSAGE_TITLE" exit 1 - ;; -esac +fi +echo "[TEST:PASS] Custom notification title was used." + +if [ "$MESSAGE_BODY" != "Backing up /tmp/test.tar.gz succeeded." ]; then + echo "[TEST:FAIL] Unexpected notification body $MESSAGE_BODY" + exit 1 +fi +echo "[TEST:PASS] Custom notification body was used." docker-compose down --volumes diff --git a/test/swarm/run.sh b/test/swarm/run.sh index 646b9e5..165dea0 100755 --- a/test/swarm/run.sh +++ b/test/swarm/run.sh @@ -29,9 +29,7 @@ if [ "$(docker ps -q | wc -l)" != "5" ]; then docker ps -a exit 1 fi - echo "[TEST:PASS] All containers running post backup." docker stack rm test_stack - docker swarm leave --force