mirror of
https://github.com/offen/docker-volume-backup.git
synced 2026-04-24 01:15:35 +02:00
Consolidate config value resolution at top level (#705)
* Consolidate env expansion at top level * Move handling of deprecated config into resolve method * Handle template interpolation in resolve method * Clean up and document resolve function
This commit is contained in:
@@ -4,12 +4,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/offen/docker-volume-backup/internal/errwrap"
|
"github.com/offen/docker-volume-backup/internal/errwrap"
|
||||||
@@ -225,3 +228,92 @@ func (c *Config) applyEnv() (func() error, error) {
|
|||||||
}
|
}
|
||||||
return unset, nil
|
return unset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolve is responsible for performing all implicit logic that transforms a configuration object
|
||||||
|
// into what is actually being used at runtime. E.g. environment variables are expanded or
|
||||||
|
// deprecated config options are transposed into their up to date successors. The caller is
|
||||||
|
// responsible for calling the returned reset function after usage of the config is done.
|
||||||
|
func (c *Config) resolve() (reset func() error, warnings []string, err error) {
|
||||||
|
reset, aErr := c.applyEnv()
|
||||||
|
if aErr != nil {
|
||||||
|
err = errwrap.Wrap(aErr, "error applying env")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BackupFilenameExpand {
|
||||||
|
c.BackupFilename = os.ExpandEnv(c.BackupFilename)
|
||||||
|
c.BackupLatestSymlink = os.ExpandEnv(c.BackupLatestSymlink)
|
||||||
|
c.BackupPruningPrefix = os.ExpandEnv(c.BackupPruningPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.EmailNotificationRecipient != "" {
|
||||||
|
emailURL := fmt.Sprintf(
|
||||||
|
"smtp://%s:%s@%s:%d/?from=%s&to=%s",
|
||||||
|
c.EmailSMTPUsername,
|
||||||
|
c.EmailSMTPPassword,
|
||||||
|
c.EmailSMTPHost,
|
||||||
|
c.EmailSMTPPort,
|
||||||
|
c.EmailNotificationSender,
|
||||||
|
c.EmailNotificationRecipient,
|
||||||
|
)
|
||||||
|
c.NotificationURLs = append(c.NotificationURLs, emailURL)
|
||||||
|
warnings = append(warnings,
|
||||||
|
"Using EMAIL_* keys for providing notification configuration has been deprecated and will be removed in the next major version.",
|
||||||
|
"Please use NOTIFICATION_URLS instead. Refer to the README for an upgrade guide.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BackupFromSnapshot {
|
||||||
|
warnings = append(warnings,
|
||||||
|
"Using BACKUP_FROM_SNAPSHOT has been deprecated and will be removed in the next major version.",
|
||||||
|
"Please use `archive-pre` and `archive-post` commands to prepare your backup sources. Refer to the documentation for an upgrade guide.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BackupStopDuringBackupLabel != "" && c.BackupStopContainerLabel != "" {
|
||||||
|
err = errwrap.Wrap(nil, "both BACKUP_STOP_DURING_BACKUP_LABEL and BACKUP_STOP_CONTAINER_LABEL have been set, cannot continue")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.BackupStopContainerLabel != "" {
|
||||||
|
warnings = append(warnings,
|
||||||
|
"Using BACKUP_STOP_CONTAINER_LABEL has been deprecated and will be removed in the next major version.",
|
||||||
|
"Please use BACKUP_STOP_DURING_BACKUP_LABEL instead. Refer to the docs for an upgrade guide.",
|
||||||
|
)
|
||||||
|
c.BackupStopDuringBackupLabel = c.BackupStopContainerLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
tmplFileName, tErr := template.New("extension").Parse(c.BackupFilename)
|
||||||
|
if tErr != nil {
|
||||||
|
err = errwrap.Wrap(tErr, "unable to parse backup file extension template")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var bf bytes.Buffer
|
||||||
|
if tErr := tmplFileName.Execute(&bf, map[string]string{
|
||||||
|
"Extension": func() string {
|
||||||
|
if c.BackupCompression == "none" {
|
||||||
|
return "tar"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("tar.%s", c.BackupCompression)
|
||||||
|
}(),
|
||||||
|
}); tErr != nil {
|
||||||
|
err = errwrap.Wrap(tErr, "error executing backup file extension template")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.BackupFilename = bf.String()
|
||||||
|
|
||||||
|
if c.AzureStorageEndpoint != "" {
|
||||||
|
endpointTemplate, tErr := template.New("endpoint").Parse(c.AzureStorageEndpoint)
|
||||||
|
if tErr != nil {
|
||||||
|
err = errwrap.Wrap(tErr, "error parsing endpoint template")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var ep bytes.Buffer
|
||||||
|
if tErr := endpointTemplate.Execute(&ep, map[string]string{"AccountName": c.AzureStorageAccountName}); tErr != nil {
|
||||||
|
err = errwrap.Wrap(tErr, "error executing endpoint template")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.AzureStorageEndpoint = fmt.Sprintf("%s/", strings.TrimSuffix(ep.String(), "/"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,12 +18,6 @@ func (s *script) createArchive() error {
|
|||||||
backupSources := s.c.BackupSources
|
backupSources := s.c.BackupSources
|
||||||
|
|
||||||
if s.c.BackupFromSnapshot {
|
if s.c.BackupFromSnapshot {
|
||||||
s.logger.Warn(
|
|
||||||
"Using BACKUP_FROM_SNAPSHOT has been deprecated and will be removed in the next major version.",
|
|
||||||
)
|
|
||||||
s.logger.Warn(
|
|
||||||
"Please use `archive-pre` and `archive-post` commands to prepare your backup sources. Refer to the documentation for an upgrade guide.",
|
|
||||||
)
|
|
||||||
backupSources = filepath.Join("/tmp", s.c.BackupSources)
|
backupSources = filepath.Join("/tmp", s.c.BackupSources)
|
||||||
// copy before compressing guard against a situation where backup folder's content are still growing.
|
// copy before compressing guard against a situation where backup folder's content are still growing.
|
||||||
s.registerHook(hookLevelPlumbing, func(error) error {
|
s.registerHook(hookLevelPlumbing, func(error) error {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func runScript(c *Config) (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
unset, err := s.c.applyEnv()
|
unset, warnings, err := s.c.resolve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrap(err, "error applying env")
|
return errwrap.Wrap(err, "error applying env")
|
||||||
}
|
}
|
||||||
@@ -52,6 +52,9 @@ func runScript(c *Config) (err error) {
|
|||||||
err = errors.Join(err, errwrap.Wrap(derr, "error unsetting environment variables"))
|
err = errors.Join(err, errwrap.Wrap(derr, "error unsetting environment variables"))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
for _, w := range warnings {
|
||||||
|
s.logger.Warn(w)
|
||||||
|
}
|
||||||
|
|
||||||
if s.c != nil && s.c.BackupJitter > 0 {
|
if s.c != nil && s.c.BackupJitter > 0 {
|
||||||
max := s.c.BackupJitter
|
max := s.c.BackupJitter
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
@@ -79,24 +78,6 @@ func (s *script) init() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
// Register notifications first so they can fire in case of other init errors.
|
// Register notifications first so they can fire in case of other init errors.
|
||||||
if s.c.EmailNotificationRecipient != "" {
|
|
||||||
emailURL := fmt.Sprintf(
|
|
||||||
"smtp://%s:%s@%s:%d/?from=%s&to=%s",
|
|
||||||
s.c.EmailSMTPUsername,
|
|
||||||
s.c.EmailSMTPPassword,
|
|
||||||
s.c.EmailSMTPHost,
|
|
||||||
s.c.EmailSMTPPort,
|
|
||||||
s.c.EmailNotificationSender,
|
|
||||||
s.c.EmailNotificationRecipient,
|
|
||||||
)
|
|
||||||
s.c.NotificationURLs = append(s.c.NotificationURLs, emailURL)
|
|
||||||
s.logger.Warn(
|
|
||||||
"Using EMAIL_* keys for providing notification configuration has been deprecated and will be removed in the next major version.",
|
|
||||||
)
|
|
||||||
s.logger.Warn(
|
|
||||||
"Please use NOTIFICATION_URLS instead. Refer to the README for an upgrade guide.",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
hookLevel, ok := hookLevels[s.c.NotificationLevel]
|
hookLevel, ok := hookLevels[s.c.NotificationLevel]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -143,30 +124,6 @@ func (s *script) init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.file = path.Join("/tmp", s.c.BackupFilename)
|
s.file = path.Join("/tmp", s.c.BackupFilename)
|
||||||
|
|
||||||
tmplFileName, tErr := template.New("extension").Parse(s.file)
|
|
||||||
if tErr != nil {
|
|
||||||
return errwrap.Wrap(tErr, "unable to parse backup file extension template")
|
|
||||||
}
|
|
||||||
|
|
||||||
var bf bytes.Buffer
|
|
||||||
if tErr := tmplFileName.Execute(&bf, map[string]string{
|
|
||||||
"Extension": func() string {
|
|
||||||
if s.c.BackupCompression == "none" {
|
|
||||||
return "tar"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("tar.%s", s.c.BackupCompression)
|
|
||||||
}(),
|
|
||||||
}); tErr != nil {
|
|
||||||
return errwrap.Wrap(tErr, "error executing backup file extension template")
|
|
||||||
}
|
|
||||||
s.file = bf.String()
|
|
||||||
|
|
||||||
if s.c.BackupFilenameExpand {
|
|
||||||
s.file = os.ExpandEnv(s.file)
|
|
||||||
s.c.BackupLatestSymlink = os.ExpandEnv(s.c.BackupLatestSymlink)
|
|
||||||
s.c.BackupPruningPrefix = os.ExpandEnv(s.c.BackupPruningPrefix)
|
|
||||||
}
|
|
||||||
s.file = timeutil.Strftime(&s.stats.StartTime, s.file)
|
s.file = timeutil.Strftime(&s.stats.StartTime, s.file)
|
||||||
|
|
||||||
_, err := os.Stat("/var/run/docker.sock")
|
_, err := os.Stat("/var/run/docker.sock")
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -113,23 +112,9 @@ func (s *script) stopContainersAndServices() (func() error, error) {
|
|||||||
return noop, errwrap.Wrap(err, "error determining swarm state")
|
return noop, errwrap.Wrap(err, "error determining swarm state")
|
||||||
}
|
}
|
||||||
|
|
||||||
labelValue := s.c.BackupStopDuringBackupLabel
|
|
||||||
if s.c.BackupStopContainerLabel != "" {
|
|
||||||
s.logger.Warn(
|
|
||||||
"Using BACKUP_STOP_CONTAINER_LABEL has been deprecated and will be removed in the next major version.",
|
|
||||||
)
|
|
||||||
s.logger.Warn(
|
|
||||||
"Please use BACKUP_STOP_DURING_BACKUP_LABEL instead. Refer to the docs for an upgrade guide.",
|
|
||||||
)
|
|
||||||
if _, ok := os.LookupEnv("BACKUP_STOP_DURING_BACKUP_LABEL"); ok {
|
|
||||||
return noop, errwrap.Wrap(nil, "both BACKUP_STOP_DURING_BACKUP_LABEL and BACKUP_STOP_CONTAINER_LABEL have been set, cannot continue")
|
|
||||||
}
|
|
||||||
labelValue = s.c.BackupStopContainerLabel
|
|
||||||
}
|
|
||||||
|
|
||||||
stopDuringBackupLabel := fmt.Sprintf(
|
stopDuringBackupLabel := fmt.Sprintf(
|
||||||
"docker-volume-backup.stop-during-backup=%s",
|
"docker-volume-backup.stop-during-backup=%s",
|
||||||
labelValue,
|
s.c.BackupStopDuringBackupLabel,
|
||||||
)
|
)
|
||||||
|
|
||||||
stopDuringBackupNoRestartLabel := fmt.Sprintf(
|
stopDuringBackupNoRestartLabel := fmt.Sprintf(
|
||||||
@@ -144,7 +129,7 @@ func (s *script) stopContainersAndServices() (func() error, error) {
|
|||||||
|
|
||||||
var containersToStop []handledContainer
|
var containersToStop []handledContainer
|
||||||
for _, c := range allContainers.Items {
|
for _, c := range allContainers.Items {
|
||||||
hasStopDuringBackupLabel, hasStopDuringBackupNoRestartLabel, err := checkStopLabels(c.Labels, labelValue, s.c.BackupStopDuringBackupNoRestartLabel)
|
hasStopDuringBackupLabel, hasStopDuringBackupNoRestartLabel, err := checkStopLabels(c.Labels, s.c.BackupStopDuringBackupLabel, s.c.BackupStopDuringBackupNoRestartLabel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return noop, errwrap.Wrap(err, "error querying for containers to stop")
|
return noop, errwrap.Wrap(err, "error querying for containers to stop")
|
||||||
}
|
}
|
||||||
@@ -169,7 +154,7 @@ func (s *script) stopContainersAndServices() (func() error, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, service := range allServices {
|
for _, service := range allServices {
|
||||||
hasStopDuringBackupLabel, hasStopDuringBackupNoRestartLabel, err := checkStopLabels(service.Spec.Labels, labelValue, s.c.BackupStopDuringBackupNoRestartLabel)
|
hasStopDuringBackupLabel, hasStopDuringBackupNoRestartLabel, err := checkStopLabels(service.Spec.Labels, s.c.BackupStopDuringBackupLabel, s.c.BackupStopDuringBackupNoRestartLabel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return noop, errwrap.Wrap(err, "error querying for services to scale down")
|
return noop, errwrap.Wrap(err, "error querying for services to scale down")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,13 @@
|
|||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||||
@@ -49,16 +46,6 @@ func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error
|
|||||||
return nil, errwrap.Wrap(nil, "using primary account key and connection string are mutually exclusive")
|
return nil, errwrap.Wrap(nil, "using primary account key and connection string are mutually exclusive")
|
||||||
}
|
}
|
||||||
|
|
||||||
endpointTemplate, err := template.New("endpoint").Parse(opts.Endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errwrap.Wrap(err, "error parsing endpoint template")
|
|
||||||
}
|
|
||||||
var ep bytes.Buffer
|
|
||||||
if err := endpointTemplate.Execute(&ep, opts); err != nil {
|
|
||||||
return nil, errwrap.Wrap(err, "error executing endpoint template")
|
|
||||||
}
|
|
||||||
normalizedEndpoint := fmt.Sprintf("%s/", strings.TrimSuffix(ep.String(), "/"))
|
|
||||||
|
|
||||||
var client *azblob.Client
|
var client *azblob.Client
|
||||||
if opts.PrimaryAccountKey != "" {
|
if opts.PrimaryAccountKey != "" {
|
||||||
cred, err := azblob.NewSharedKeyCredential(opts.AccountName, opts.PrimaryAccountKey)
|
cred, err := azblob.NewSharedKeyCredential(opts.AccountName, opts.PrimaryAccountKey)
|
||||||
@@ -66,11 +53,12 @@ func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error
|
|||||||
return nil, errwrap.Wrap(err, "error creating shared key Azure credential")
|
return nil, errwrap.Wrap(err, "error creating shared key Azure credential")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err = azblob.NewClientWithSharedKeyCredential(normalizedEndpoint, cred, nil)
|
client, err = azblob.NewClientWithSharedKeyCredential(opts.Endpoint, cred, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrap(err, "error creating azure client from primary account key")
|
return nil, errwrap.Wrap(err, "error creating azure client from primary account key")
|
||||||
}
|
}
|
||||||
} else if opts.ConnectionString != "" {
|
} else if opts.ConnectionString != "" {
|
||||||
|
var err error
|
||||||
client, err = azblob.NewClientFromConnectionString(opts.ConnectionString, nil)
|
client, err = azblob.NewClientFromConnectionString(opts.ConnectionString, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrap(err, "error creating azure client from connection string")
|
return nil, errwrap.Wrap(err, "error creating azure client from connection string")
|
||||||
@@ -80,7 +68,7 @@ func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrap(err, "error creating managed identity credential")
|
return nil, errwrap.Wrap(err, "error creating managed identity credential")
|
||||||
}
|
}
|
||||||
client, err = azblob.NewClient(normalizedEndpoint, cred, nil)
|
client, err = azblob.NewClient(opts.Endpoint, cred, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrap(err, "error creating azure client from managed identity")
|
return nil, errwrap.Wrap(err, "error creating azure client from managed identity")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user