diff --git a/cmd/backup/command.go b/cmd/backup/command.go index 10fa0e16..662a466d 100644 --- a/cmd/backup/command.go +++ b/cmd/backup/command.go @@ -36,8 +36,17 @@ func (c *command) runAsCommand() error { } for _, config := range configurations { - if err := runScript(config); err != nil { - return errwrap.Wrap(err, "error running script") + switch config.ExecutionMode { + case "process": + if err := runScript(config); err != nil { + return errwrap.Wrap(err, "error running script") + } + case "container": + if err := runInContainer(config); err != nil { + return errwrap.Wrap(err, "error spawning container") + } + default: + return errwrap.Wrap(nil, fmt.Sprintf("unknown execution mode %s", config.ExecutionMode)) } } @@ -110,16 +119,39 @@ func (c *command) schedule(strategy configStrategy) error { ), ) - if err := runScript(config); err != nil { + switch config.ExecutionMode { + case "process": + if err := runScript(config); err != nil { + c.logger.Error( + fmt.Sprintf( + "Unexpected error running schedule %s: %v", + config.BackupCronExpression, + errwrap.Unwrap(err), + ), + "error", + err, + ) + } + case "container": + if err := runInContainer(config); err != nil { + c.logger.Error( + fmt.Sprintf( + "Unexpected error running schedule %s in container: %v", + config.BackupCronExpression, + errwrap.Unwrap(err), + ), + "error", + err, + ) + } + default: c.logger.Error( fmt.Sprintf( - "Unexpected error running schedule %s: %v", - config.BackupCronExpression, - errwrap.Unwrap(err), + "Unkown execution mode %s, please check your configuration", + config.ExecutionMode, ), - "error", - err, ) + return } }) diff --git a/cmd/backup/config.go b/cmd/backup/config.go index 08232a8b..35605a88 100644 --- a/cmd/backup/config.go +++ b/cmd/backup/config.go @@ -30,7 +30,6 @@ type Config struct { AwsIamRoleEndpoint string `split_words:"true"` AwsPartSize int64 `split_words:"true"` BackupCompression CompressionType `split_words:"true" default:"gz"` - GzipParallelism WholeNumber `split_words:"true" default:"1"` BackupSources string `split_words:"true" default:"/backup"` BackupFilename string `split_words:"true" default:"backup-%Y-%m-%dT%H-%M-%S.{{ .Extension }}"` BackupFilenameExpand bool `split_words:"true"` @@ -46,6 +45,7 @@ type Config struct { BackupFromSnapshot bool `split_words:"true"` BackupExcludeRegexp RegexpDecoder `split_words:"true"` BackupSkipBackendsFromPrune []string `split_words:"true"` + GzipParallelism WholeNumber `split_words:"true" default:"1"` GpgPassphrase string `split_words:"true"` GpgPublicKeyRing string `split_words:"true"` AgePassphrase string `split_words:"true"` @@ -73,6 +73,7 @@ type Config struct { ExecLabel string `split_words:"true"` ExecForwardOutput bool `split_words:"true"` LockTimeout time.Duration `split_words:"true" default:"60m"` + ExecutionMode string `split_words:"true" default:"process"` AzureStorageAccountName string `split_words:"true"` AzureStoragePrimaryAccountKey string `split_words:"true"` AzureStorageConnectionString string `split_words:"true"` diff --git a/cmd/backup/container.go b/cmd/backup/container.go new file mode 100644 index 00000000..482c4156 --- /dev/null +++ b/cmd/backup/container.go @@ -0,0 +1,5 @@ +package main + +func runInContainer(config *Config) error { + return nil +} diff --git a/test/container/docker-compose.yml b/test/container/docker-compose.yml new file mode 100644 index 00000000..839d4f75 --- /dev/null +++ b/test/container/docker-compose.yml @@ -0,0 +1,22 @@ +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + restart: always + environment: + BACKUP_FILENAME: test.tar.gz + BACKUP_CRON_EXPRESSION: 0 0 5 31 2 ? + EXECUTION_MODE: container + volumes: + - app_data:/backup/app_data:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + - ${LOCAL_DIR:-./local}:/archive + + offen: + image: offen/offen:latest + labels: + - docker-volume-backup.stop-during-backup=true + volumes: + - app_data:/var/opt/offen + +volumes: + app_data: diff --git a/test/container/run.sh b/test/container/run.sh new file mode 100755 index 00000000..b3b8230a --- /dev/null +++ b/test/container/run.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e + +cd "$(dirname "$0")" +. ../util.sh +current_test=$(basename $(pwd)) + +export LOCAL_DIR=$(mktemp -d) + +docker compose up -d --quiet-pull +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 + +expect_running_containers "2" + +tmp_dir=$(mktemp -d) +tar -xvf "$LOCAL_DIR/test.tar.gz" -C $tmp_dir +if [ ! -f "$tmp_dir/backup/app_data/offen.db" ]; then + fail "Could not find expected file in untared archive." +fi +rm -f "$LOCAL_DIR/test-hostnametoken.tar.gz"