Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions cmd/backup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
package main

import (
"bytes"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"text/template"
"time"

"github.com/offen/docker-volume-backup/internal/errwrap"
Expand Down Expand Up @@ -225,3 +228,92 @@ func (c *Config) applyEnv() (func() error, error) {
}
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
}
6 changes: 0 additions & 6 deletions cmd/backup/create_archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ func (s *script) createArchive() error {
backupSources := s.c.BackupSources

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)
// copy before compressing guard against a situation where backup folder's content are still growing.
s.registerHook(hookLevelPlumbing, func(error) error {
Expand Down
5 changes: 4 additions & 1 deletion cmd/backup/run_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func runScript(c *Config) (err error) {
}
}()

unset, err := s.c.applyEnv()
unset, warnings, err := s.c.resolve()
if err != nil {
return errwrap.Wrap(err, "error applying env")
}
Expand All @@ -52,6 +52,9 @@ func runScript(c *Config) (err error) {
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 {
max := s.c.BackupJitter
Expand Down
43 changes: 0 additions & 43 deletions cmd/backup/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package main

import (
"bytes"
"fmt"
"log/slog"
"os"
Expand Down Expand Up @@ -79,24 +78,6 @@ func (s *script) init() error {
return nil
})
// 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]
if !ok {
Expand Down Expand Up @@ -143,30 +124,6 @@ func (s *script) init() error {
}

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)

_, err := os.Stat("/var/run/docker.sock")
Expand Down
21 changes: 3 additions & 18 deletions cmd/backup/stop_restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io"
"os"
"sync"
"time"

Expand Down Expand Up @@ -113,23 +112,9 @@ func (s *script) stopContainersAndServices() (func() error, error) {
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(
"docker-volume-backup.stop-during-backup=%s",
labelValue,
s.c.BackupStopDuringBackupLabel,
)

stopDuringBackupNoRestartLabel := fmt.Sprintf(
Expand All @@ -144,7 +129,7 @@ func (s *script) stopContainersAndServices() (func() error, error) {

var containersToStop []handledContainer
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 {
return noop, errwrap.Wrap(err, "error querying for containers to stop")
}
Expand All @@ -169,7 +154,7 @@ func (s *script) stopContainersAndServices() (func() error, error) {
}

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 {
return noop, errwrap.Wrap(err, "error querying for services to scale down")
}
Expand Down
18 changes: 3 additions & 15 deletions internal/storage/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@
package azure

import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"sync"
"text/template"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
Expand Down Expand Up @@ -49,28 +46,19 @@ 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")
}

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
if opts.PrimaryAccountKey != "" {
cred, err := azblob.NewSharedKeyCredential(opts.AccountName, opts.PrimaryAccountKey)
if err != nil {
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 {
return nil, errwrap.Wrap(err, "error creating azure client from primary account key")
}
} else if opts.ConnectionString != "" {
var err error
client, err = azblob.NewClientFromConnectionString(opts.ConnectionString, nil)
if err != nil {
return nil, errwrap.Wrap(err, "error creating azure client from connection string")
Expand All @@ -80,7 +68,7 @@ func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error
if err != nil {
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 {
return nil, errwrap.Wrap(err, "error creating azure client from managed identity")
}
Expand Down