Handle configuration errors for Azure storage upfront

This commit is contained in:
Frederik Ring 2024-08-11 15:08:06 +02:00
parent 8a64da4b0b
commit d8aa6db3f5
4 changed files with 115 additions and 92 deletions

View File

@ -36,6 +36,9 @@ func (c *command) runAsCommand() error {
} }
for _, config := range configurations { for _, config := range configurations {
if err := config.validate(); err != nil {
return errwrap.Wrap(err, "error validating config")
}
if err := runScript(config); err != nil { if err := runScript(config); err != nil {
return errwrap.Wrap(err, "error running script") return errwrap.Wrap(err, "error running script")
} }
@ -101,6 +104,12 @@ func (c *command) schedule(strategy configStrategy) error {
} }
for _, cfg := range configurations { for _, cfg := range configurations {
if err := cfg.validate(); err != nil {
return errwrap.Wrap(
err,
fmt.Sprintf("error validating config for schedule %s", cfg.BackupCronExpression),
)
}
config := cfg config := cfg
id, err := c.cr.AddFunc(config.BackupCronExpression, func() { id, err := c.cr.AddFunc(config.BackupCronExpression, func() {
c.logger.Info( c.logger.Info(

View File

@ -12,6 +12,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/offen/docker-volume-backup/internal/errwrap" "github.com/offen/docker-volume-backup/internal/errwrap"
) )
@ -77,7 +78,7 @@ type Config struct {
AzureStorageContainerName string `split_words:"true"` AzureStorageContainerName string `split_words:"true"`
AzureStoragePath string `split_words:"true"` AzureStoragePath string `split_words:"true"`
AzureStorageEndpoint string `split_words:"true" default:"https://{{ .AccountName }}.blob.core.windows.net/"` AzureStorageEndpoint string `split_words:"true" default:"https://{{ .AccountName }}.blob.core.windows.net/"`
AzureStorageAccessTier string `split_words:"true"` AzureStorageAccessTier AzureStorageAccessTier `split_words:"true"`
DropboxEndpoint string `split_words:"true" default:"https://api.dropbox.com/"` DropboxEndpoint string `split_words:"true" default:"https://api.dropbox.com/"`
DropboxOAuth2Endpoint string `envconfig:"DROPBOX_OAUTH2_ENDPOINT" default:"https://api.dropbox.com/"` DropboxOAuth2Endpoint string `envconfig:"DROPBOX_OAUTH2_ENDPOINT" default:"https://api.dropbox.com/"`
DropboxRefreshToken string `split_words:"true"` DropboxRefreshToken string `split_words:"true"`
@ -89,6 +90,13 @@ type Config struct {
additionalEnvVars map[string]string additionalEnvVars map[string]string
} }
func (c *Config) validate() error {
if c.AzureStoragePrimaryAccountKey != "" && c.AzureStorageConnectionString != "" {
return errwrap.Wrap(nil, "using azure primary account key and connection string are mutually exclusive")
}
return nil
}
type CompressionType string type CompressionType string
func (c *CompressionType) Decode(v string) error { func (c *CompressionType) Decode(v string) error {
@ -180,6 +188,30 @@ func (n *WholeNumber) Int() int {
return int(*n) return int(*n)
} }
type AzureStorageAccessTier string
func (t *AzureStorageAccessTier) Decode(v string) error {
if v == "" {
*t = ""
return nil
}
for _, a := range blob.PossibleAccessTierValues() {
if string(a) == v {
*t = AzureStorageAccessTier(v)
return nil
}
}
return errwrap.Wrap(nil, fmt.Sprintf("%s is not a possible access tier value", v))
}
func (t *AzureStorageAccessTier) AccessTier() *blob.AccessTier {
if *t == "" {
return nil
}
a := blob.AccessTier(*t)
return &a
}
type envVarLookup struct { type envVarLookup struct {
ok bool ok bool
key string key string

View File

@ -199,7 +199,7 @@ func (s *script) init() error {
Endpoint: s.c.AzureStorageEndpoint, Endpoint: s.c.AzureStorageEndpoint,
RemotePath: s.c.AzureStoragePath, RemotePath: s.c.AzureStoragePath,
ConnectionString: s.c.AzureStorageConnectionString, ConnectionString: s.c.AzureStorageConnectionString,
AccessTier: s.c.AzureStorageAccessTier, AccessTier: s.c.AzureStorageAccessTier.AccessTier(),
} }
azureBackend, err := azure.NewStorageBackend(azureConfig, logFunc) azureBackend, err := azure.NewStorageBackend(azureConfig, logFunc)
if err != nil { if err != nil {

View File

@ -39,15 +39,11 @@ type Config struct {
ConnectionString string ConnectionString string
Endpoint string Endpoint string
RemotePath string RemotePath string
AccessTier string AccessTier *blob.AccessTier
} }
// NewStorageBackend creates and initializes a new Azure Blob Storage backend. // NewStorageBackend creates and initializes a new Azure Blob Storage backend.
func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error) { func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error) {
if opts.PrimaryAccountKey != "" && opts.ConnectionString != "" {
return nil, errwrap.Wrap(nil, "using primary account key and connection string are mutually exclusive")
}
endpointTemplate, err := template.New("endpoint").Parse(opts.Endpoint) endpointTemplate, err := template.New("endpoint").Parse(opts.Endpoint)
if err != nil { if err != nil {
return nil, errwrap.Wrap(err, "error parsing endpoint template") return nil, errwrap.Wrap(err, "error parsing endpoint template")
@ -85,25 +81,11 @@ func NewStorageBackend(opts Config, logFunc storage.Log) (storage.Backend, error
} }
} }
var uploadStreamOptions *blockblob.UploadStreamOptions
if opts.AccessTier != "" {
var found bool
for _, t := range blob.PossibleAccessTierValues() {
if string(t) == opts.AccessTier {
found = true
uploadStreamOptions = &blockblob.UploadStreamOptions{
AccessTier: &t,
}
}
}
if !found {
return nil, errwrap.Wrap(nil, fmt.Sprintf("%s is not a possible access tier value", opts.AccessTier))
}
}
storage := azureBlobStorage{ storage := azureBlobStorage{
client: client, client: client,
uploadStreamOptions: uploadStreamOptions, uploadStreamOptions: &blockblob.UploadStreamOptions{
AccessTier: opts.AccessTier,
},
containerName: opts.ContainerName, containerName: opts.ContainerName,
StorageBackend: &storage.StorageBackend{ StorageBackend: &storage.StorageBackend{
DestinationPath: opts.RemotePath, DestinationPath: opts.RemotePath,