From af35c763a5f192c2056d68514d827c7bf96eb766 Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Sun, 21 Dec 2025 21:04:25 +0100 Subject: [PATCH 1/6] Add "show-config" subcommand --- cmd/backup/main.go | 4 ++++ cmd/backup/show_config.go | 31 ++++++++++++++++++++++++++++++ docs/how-tos/show-configuration.md | 24 +++++++++++++++++++++++ docs/reference/index.md | 1 + 4 files changed, 60 insertions(+) create mode 100644 cmd/backup/show_config.go create mode 100644 docs/how-tos/show-configuration.md diff --git a/cmd/backup/main.go b/cmd/backup/main.go index 47c353d..f84b60e 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -13,6 +13,10 @@ func main() { flag.Parse() c := newCommand() + if flag.Arg(0) == "show-config" { + c.must(runShowConfig()) + return + } if *foreground { opts := foregroundOpts{ profileCronExpression: *profile, diff --git a/cmd/backup/show_config.go b/cmd/backup/show_config.go new file mode 100644 index 0000000..eb7e614 --- /dev/null +++ b/cmd/backup/show_config.go @@ -0,0 +1,31 @@ +// Copyright 2025 - offen.software +// SPDX-License-Identifier: MPL-2.0 + +package main + +import ( + "fmt" + "regexp" + + "github.com/offen/docker-volume-backup/internal/errwrap" +) + +func runShowConfig() error { + configurations, err := sourceConfiguration(configStrategyConfd) + if err != nil { + fmt.Printf("error sourcing configuration: %v\n", err) // print error to stdout for debugging + return errwrap.Wrap(err, "error sourcing configuration") + } + + for _, config := range configurations { + if config == nil { + fmt.Println("source=\n") + continue + } + // insert line breaks before each field name, assuming field names start with uppercase letters + formatted := regexp.MustCompile(`\s([A-Z])`).ReplaceAllString(fmt.Sprintf("%+v", *config), "\n$1") + fmt.Printf("source=%s\n%s\n", config.source, formatted) + } + + return nil +} diff --git a/docs/how-tos/show-configuration.md b/docs/how-tos/show-configuration.md new file mode 100644 index 0000000..82775a4 --- /dev/null +++ b/docs/how-tos/show-configuration.md @@ -0,0 +1,24 @@ +--- +title: Show loaded configuration +layout: default +parent: How Tos +nav_order: 8 +--- + +# Show loaded configuration + +You can print the configuration that `docker-volume-backup` has picked up without running a backup: + +```console +docker exec backup show-config +``` + +If configuration sourcing fails, the error is printed to stdout to aid debugging. + +If you want to test a one-off value, pass it directly: + +```console +docker exec -e BACKUP_SOURCES=/backup -e NOTIFICATION_URLS=stdout:// backup show-config +``` + +Note: output includes secrets exactly as loaded. diff --git a/docs/reference/index.md b/docs/reference/index.md index 6e4f48e..027ed0b 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -19,6 +19,7 @@ You can work around this by either updating `docker-compose` or unquoting your c You can populate below template according to your requirements and use it as your `env_file`. The values for each key currently match its default. +If you need to confirm what the container actually loaded, see [Show loaded configuration](../how-tos/show-configuration.md). {% raw %} ``` From f615c2b3fa731697859c2ffcdfd7667c03b672c8 Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Sun, 21 Dec 2025 21:35:16 +0100 Subject: [PATCH 2/6] Add integration tests --- test/show-config/conf.d/01show-config.env | 2 + test/show-config/docker-compose.confd.yml | 6 +++ test/show-config/docker-compose.yml | 7 +++ test/show-config/run.sh | 63 +++++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 test/show-config/conf.d/01show-config.env create mode 100644 test/show-config/docker-compose.confd.yml create mode 100644 test/show-config/docker-compose.yml create mode 100755 test/show-config/run.sh diff --git a/test/show-config/conf.d/01show-config.env b/test/show-config/conf.d/01show-config.env new file mode 100644 index 0000000..7bf7819 --- /dev/null +++ b/test/show-config/conf.d/01show-config.env @@ -0,0 +1,2 @@ +BACKUP_SOURCES=/conf-backup +NOTIFICATION_URLS_FILE=/run/secrets/notification_urls diff --git a/test/show-config/docker-compose.confd.yml b/test/show-config/docker-compose.confd.yml new file mode 100644 index 0000000..4adcebc --- /dev/null +++ b/test/show-config/docker-compose.confd.yml @@ -0,0 +1,6 @@ +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + volumes: + - ${CONF_DIR}:/etc/dockervolumebackup/conf.d:ro + - ${SECRET_FILE}:/run/secrets/notification_urls:ro diff --git a/test/show-config/docker-compose.yml b/test/show-config/docker-compose.yml new file mode 100644 index 0000000..1b28c96 --- /dev/null +++ b/test/show-config/docker-compose.yml @@ -0,0 +1,7 @@ +services: + backup: + image: offen/docker-volume-backup:${TEST_VERSION:-canary} + environment: + BACKUP_SOURCES: /backup + NOTIFICATION_URLS: "stdout://" + AWS_S3_BUCKET_NAME: example-bucket diff --git a/test/show-config/run.sh b/test/show-config/run.sh new file mode 100755 index 0000000..3e0586e --- /dev/null +++ b/test/show-config/run.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +set -e + +cd "$(dirname "$0")" +. ../util.sh +current_test=$(basename $(pwd)) + +info "show-config with environment variables" +docker compose up -d --quiet-pull +logs=$(docker compose exec -T backup backup show-config) + +echo "$logs" + +if ! echo "$logs" | grep -q "source=from environment"; then + fail "Missing source line." +fi +pass "Source line present." + +if ! echo "$logs" | grep -q "BackupSources:/backup"; then + fail "Missing BACKUP_SOURCES in output." +fi +pass "BACKUP_SOURCES present." + +if ! echo "$logs" | grep -q "NotificationURLs:\[stdout://\]"; then + fail "Missing NOTIFICATION_URLS in output." +fi +pass "NOTIFICATION_URLS present." + +if ! echo "$logs" | grep -q "AwsS3BucketName:example-bucket"; then + fail "Missing AWS_S3_BUCKET_NAME in output." +fi +pass "AWS_S3_BUCKET_NAME present." + +docker compose down + +info "show-config with conf.d and _FILE" +export CONF_DIR=$(pwd)/conf.d +export SECRET_FILE=$(mktemp) +printf "stdout://\n" > "$SECRET_FILE" + +docker compose -f docker-compose.confd.yml up -d --quiet-pull +logs=$(docker compose -f docker-compose.confd.yml exec -T backup backup show-config) + +echo "$logs" + +if ! echo "$logs" | grep -q "source=01show-config.env"; then + fail "Missing conf.d source line." +fi +pass "conf.d source line present." + +if ! echo "$logs" | grep -q "BackupSources:/conf-backup"; then + fail "Missing conf.d BACKUP_SOURCES in output." +fi +pass "conf.d BACKUP_SOURCES present." + +if ! echo "$logs" | grep -q "NotificationURLs:\\[stdout://"; then + fail "Missing conf.d NOTIFICATION_URLS in output." +fi +pass "conf.d NOTIFICATION_URLS present." + +docker compose -f docker-compose.confd.yml down +rm -f "$SECRET_FILE" From 13e54e1ba0e0795acdb337d5c37d1553f389d40b Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Sat, 27 Dec 2025 12:14:39 +0100 Subject: [PATCH 3/6] Handle wrong additional args --- cmd/backup/main.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/backup/main.go b/cmd/backup/main.go index f84b60e..5f3435c 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -11,11 +11,17 @@ func main() { foreground := flag.Bool("foreground", false, "run the tool in the foreground") profile := flag.String("profile", "", "collect runtime metrics and log them periodically on the given cron expression") flag.Parse() - + additionalArgs := flag.Args() c := newCommand() - if flag.Arg(0) == "show-config" { - c.must(runShowConfig()) - return + + if len(additionalArgs) > 0 { + switch additionalArgs[0] { + case "show-config": + c.must(runShowConfig()) + return + default: + panic("unknown command: " + additionalArgs[0]) + } } if *foreground { opts := foregroundOpts{ From a826828d7649d7a49ad2f4edeaaa83f996b5adab Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Thu, 1 Jan 2026 13:49:19 +0100 Subject: [PATCH 4/6] Support "BackupFilenameExpand" option --- cmd/backup/show_config.go | 15 +++++++++++++++ test/show-config/docker-compose.yml | 3 +++ test/show-config/run.sh | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/cmd/backup/show_config.go b/cmd/backup/show_config.go index eb7e614..51720f1 100644 --- a/cmd/backup/show_config.go +++ b/cmd/backup/show_config.go @@ -5,6 +5,7 @@ package main import ( "fmt" + "os" "regexp" "github.com/offen/docker-volume-backup/internal/errwrap" @@ -22,6 +23,20 @@ func runShowConfig() error { fmt.Println("source=\n") continue } + if config.BackupFilenameExpand { + unset, err := config.applyEnv() + if err != nil { + fmt.Printf("error applying env: %v\n", err) // print error to stdout for debugging + return errwrap.Wrap(err, "error applying env") + } + config.BackupFilename = os.ExpandEnv(config.BackupFilename) + config.BackupLatestSymlink = os.ExpandEnv(config.BackupLatestSymlink) + config.BackupPruningPrefix = os.ExpandEnv(config.BackupPruningPrefix) + if err := unset(); err != nil { + fmt.Printf("error unsetting env: %v\n", err) // print error to stdout for debugging + return errwrap.Wrap(err, "error unsetting environment variables") + } + } // insert line breaks before each field name, assuming field names start with uppercase letters formatted := regexp.MustCompile(`\s([A-Z])`).ReplaceAllString(fmt.Sprintf("%+v", *config), "\n$1") fmt.Printf("source=%s\n%s\n", config.source, formatted) diff --git a/test/show-config/docker-compose.yml b/test/show-config/docker-compose.yml index 1b28c96..6ceadca 100644 --- a/test/show-config/docker-compose.yml +++ b/test/show-config/docker-compose.yml @@ -2,6 +2,9 @@ services: backup: image: offen/docker-volume-backup:${TEST_VERSION:-canary} environment: + BACKUP_FILENAME: "backup-$${BACKUP_TAG}.tar" + BACKUP_FILENAME_EXPAND: true + BACKUP_TAG: expanded BACKUP_SOURCES: /backup NOTIFICATION_URLS: "stdout://" AWS_S3_BUCKET_NAME: example-bucket diff --git a/test/show-config/run.sh b/test/show-config/run.sh index 3e0586e..5979f7b 100755 --- a/test/show-config/run.sh +++ b/test/show-config/run.sh @@ -22,6 +22,11 @@ if ! echo "$logs" | grep -q "BackupSources:/backup"; then fi pass "BACKUP_SOURCES present." +if ! echo "$logs" | grep -q "BackupFilename:backup-expanded.tar"; then + fail "Missing expanded BACKUP_FILENAME in output." +fi +pass "Expanded BACKUP_FILENAME present." + if ! echo "$logs" | grep -q "NotificationURLs:\[stdout://\]"; then fail "Missing NOTIFICATION_URLS in output." fi From 17f1e33b8a40aa6a63ac3e81f2fd890bb846bb3f Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Thu, 1 Jan 2026 17:14:03 +0100 Subject: [PATCH 5/6] Rename to print-config --- cmd/backup/main.go | 4 ++-- cmd/backup/{show_config.go => print_config.go} | 2 +- .../{show-configuration.md => print-configuration.md} | 6 +++--- docs/reference/index.md | 2 +- .../conf.d/01print-config.env} | 0 .../docker-compose.confd.yml | 0 test/{show-config => print-config}/docker-compose.yml | 0 test/{show-config => print-config}/run.sh | 10 +++++----- 8 files changed, 12 insertions(+), 12 deletions(-) rename cmd/backup/{show_config.go => print_config.go} (98%) rename docs/how-tos/{show-configuration.md => print-configuration.md} (80%) rename test/{show-config/conf.d/01show-config.env => print-config/conf.d/01print-config.env} (100%) rename test/{show-config => print-config}/docker-compose.confd.yml (100%) rename test/{show-config => print-config}/docker-compose.yml (100%) rename test/{show-config => print-config}/run.sh (87%) diff --git a/cmd/backup/main.go b/cmd/backup/main.go index 5f3435c..e939427 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -16,8 +16,8 @@ func main() { if len(additionalArgs) > 0 { switch additionalArgs[0] { - case "show-config": - c.must(runShowConfig()) + case "print-config": + c.must(runPrintConfig()) return default: panic("unknown command: " + additionalArgs[0]) diff --git a/cmd/backup/show_config.go b/cmd/backup/print_config.go similarity index 98% rename from cmd/backup/show_config.go rename to cmd/backup/print_config.go index 51720f1..2deaf4d 100644 --- a/cmd/backup/show_config.go +++ b/cmd/backup/print_config.go @@ -11,7 +11,7 @@ import ( "github.com/offen/docker-volume-backup/internal/errwrap" ) -func runShowConfig() error { +func runPrintConfig() error { configurations, err := sourceConfiguration(configStrategyConfd) if err != nil { fmt.Printf("error sourcing configuration: %v\n", err) // print error to stdout for debugging diff --git a/docs/how-tos/show-configuration.md b/docs/how-tos/print-configuration.md similarity index 80% rename from docs/how-tos/show-configuration.md rename to docs/how-tos/print-configuration.md index 82775a4..09f284a 100644 --- a/docs/how-tos/show-configuration.md +++ b/docs/how-tos/print-configuration.md @@ -5,12 +5,12 @@ parent: How Tos nav_order: 8 --- -# Show loaded configuration +# Print loaded configuration You can print the configuration that `docker-volume-backup` has picked up without running a backup: ```console -docker exec backup show-config +docker exec backup print-config ``` If configuration sourcing fails, the error is printed to stdout to aid debugging. @@ -18,7 +18,7 @@ If configuration sourcing fails, the error is printed to stdout to aid debugging If you want to test a one-off value, pass it directly: ```console -docker exec -e BACKUP_SOURCES=/backup -e NOTIFICATION_URLS=stdout:// backup show-config +docker exec -e BACKUP_SOURCES=/backup -e NOTIFICATION_URLS=stdout:// backup print-config ``` Note: output includes secrets exactly as loaded. diff --git a/docs/reference/index.md b/docs/reference/index.md index 027ed0b..0974123 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -19,7 +19,7 @@ You can work around this by either updating `docker-compose` or unquoting your c You can populate below template according to your requirements and use it as your `env_file`. The values for each key currently match its default. -If you need to confirm what the container actually loaded, see [Show loaded configuration](../how-tos/show-configuration.md). +If you need to confirm what the container actually loaded, see [Show loaded configuration](../how-tos/print-configuration.md). {% raw %} ``` diff --git a/test/show-config/conf.d/01show-config.env b/test/print-config/conf.d/01print-config.env similarity index 100% rename from test/show-config/conf.d/01show-config.env rename to test/print-config/conf.d/01print-config.env diff --git a/test/show-config/docker-compose.confd.yml b/test/print-config/docker-compose.confd.yml similarity index 100% rename from test/show-config/docker-compose.confd.yml rename to test/print-config/docker-compose.confd.yml diff --git a/test/show-config/docker-compose.yml b/test/print-config/docker-compose.yml similarity index 100% rename from test/show-config/docker-compose.yml rename to test/print-config/docker-compose.yml diff --git a/test/show-config/run.sh b/test/print-config/run.sh similarity index 87% rename from test/show-config/run.sh rename to test/print-config/run.sh index 5979f7b..0f822c0 100755 --- a/test/show-config/run.sh +++ b/test/print-config/run.sh @@ -6,9 +6,9 @@ cd "$(dirname "$0")" . ../util.sh current_test=$(basename $(pwd)) -info "show-config with environment variables" +info "print-config with environment variables" docker compose up -d --quiet-pull -logs=$(docker compose exec -T backup backup show-config) +logs=$(docker compose exec -T backup backup print-config) echo "$logs" @@ -39,17 +39,17 @@ pass "AWS_S3_BUCKET_NAME present." docker compose down -info "show-config with conf.d and _FILE" +info "print-config with conf.d and _FILE" export CONF_DIR=$(pwd)/conf.d export SECRET_FILE=$(mktemp) printf "stdout://\n" > "$SECRET_FILE" docker compose -f docker-compose.confd.yml up -d --quiet-pull -logs=$(docker compose -f docker-compose.confd.yml exec -T backup backup show-config) +logs=$(docker compose -f docker-compose.confd.yml exec -T backup backup print-config) echo "$logs" -if ! echo "$logs" | grep -q "source=01show-config.env"; then +if ! echo "$logs" | grep -q "source=01print-config.env"; then fail "Missing conf.d source line." fi pass "conf.d source line present." From 6b95191f8b1f102289baec2c1ac1829627a566b2 Mon Sep 17 00:00:00 2001 From: Lennard Schwarz Date: Thu, 1 Jan 2026 17:14:22 +0100 Subject: [PATCH 6/6] Remove config nil check as it will never be nil --- cmd/backup/print_config.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd/backup/print_config.go b/cmd/backup/print_config.go index 2deaf4d..11bc3a2 100644 --- a/cmd/backup/print_config.go +++ b/cmd/backup/print_config.go @@ -19,10 +19,6 @@ func runPrintConfig() error { } for _, config := range configurations { - if config == nil { - fmt.Println("source=\n") - continue - } if config.BackupFilenameExpand { unset, err := config.applyEnv() if err != nil {