From a8d2013b3997b36e49c7638c204840c61baea376 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Tue, 13 Feb 2024 21:33:13 +0100 Subject: [PATCH] Move fallback to config sourcing --- cmd/backup/config.go | 17 ++++++++++------- cmd/backup/config_provider.go | 15 +++++++++++++-- cmd/backup/main.go | 20 +++++++++++--------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/cmd/backup/config.go b/cmd/backup/config.go index ef3e5aad..a199dacf 100644 --- a/cmd/backup/config.go +++ b/cmd/backup/config.go @@ -181,19 +181,22 @@ type envVarLookup struct { value string } +// applyEnv sets the values in `additionalEnvVars` as environment variables. +// It returns a function that reverts all values that have been set to its +// previous state. func (c *Config) applyEnv() (func() error, error) { lookups := []envVarLookup{} unset := func() error { for _, lookup := range lookups { - if lookup.ok { - if err := os.Setenv(lookup.key, lookup.value); err != nil { - return fmt.Errorf("(*Config).injectEnv: error setting back env var %s: %w", lookup.key, err) - } - } else { + if !lookup.ok { if err := os.Unsetenv(lookup.key); err != nil { - return fmt.Errorf("(*Config).injectEnv: error unsetting env var %s: %w", lookup.key, err) + return fmt.Errorf("(*Config).applyEnv: error unsetting env var %s: %w", lookup.key, err) } + continue + } + if err := os.Setenv(lookup.key, lookup.value); err != nil { + return fmt.Errorf("(*Config).applyEnv: error setting back env var %s: %w", lookup.key, err) } } return nil @@ -203,7 +206,7 @@ func (c *Config) applyEnv() (func() error, error) { current, ok := os.LookupEnv(key) lookups = append(lookups, envVarLookup{ok: ok, key: key, value: current}) if err := os.Setenv(key, value); err != nil { - return unset, fmt.Errorf("(*Config).injectEnv: error setting env var: %w", err) + return unset, fmt.Errorf("(*Config).applyEnv: error setting env var: %w", err) } } return unset, nil diff --git a/cmd/backup/config_provider.go b/cmd/backup/config_provider.go index 041ea175..b0a2755f 100644 --- a/cmd/backup/config_provider.go +++ b/cmd/backup/config_provider.go @@ -19,21 +19,32 @@ const ( configStrategyConfd configStrategy = "confd" ) +// sourceConfiguration returns a list of config objects using the given +// strategy. It should be the single entrypoint for retrieving configuration +// for all consumers. func sourceConfiguration(strategy configStrategy) ([]*Config, error) { switch strategy { case configStrategyEnv: c, err := loadConfigFromEnvVars() return []*Config{c}, err case configStrategyConfd: - return loadConfigsFromEnvFiles("/etc/dockervolumebackup/conf.d") + cs, err := loadConfigsFromEnvFiles("/etc/dockervolumebackup/conf.d") + if err != nil { + if os.IsNotExist(err) { + return sourceConfiguration(configStrategyEnv) + } + return nil, fmt.Errorf("sourceConfiguration: error loading config files: %w", err) + } + return cs, nil default: - return nil, fmt.Errorf("newConfig: received unknown config strategy: %v", strategy) + return nil, fmt.Errorf("sourceConfiguration: received unknown config strategy: %v", strategy) } } // envProxy is a function that mimics os.LookupEnv but can read values from any other source type envProxy func(string) (string, bool) +// loadConfig creates a config object using the given lookup function func loadConfig(lookup envProxy) (*Config, error) { envconfig.Lookup = func(key string) (string, bool) { value, okValue := lookup(key) diff --git a/cmd/backup/main.go b/cmd/backup/main.go index 86c6f69a..2e29d53e 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -37,6 +37,8 @@ func newCommand() *command { } } +// must exits the program when passed an error. It should be the only +// place where the application exits forcefully. func (c *command) must(err error) { if err != nil { c.logger.Error( @@ -48,6 +50,10 @@ func (c *command) must(err error) { } } +// runScript instantiates a new script object and orchestrates a backup run. +// To ensure it runs mutually exclusive a global file lock is acquired before +// it starts running. Any panic within the script will be recovered and returned +// as an error. func runScript(c *Config) (err error) { defer func() { if derr := recover(); derr != nil { @@ -139,6 +145,8 @@ func runScript(c *Config) (err error) { }() } +// runInForeground starts the program as a long running process, scheduling +// a job for each configuration that is available. func (c *command) runInForeground(profileCronExpression string) error { cr := cron.New( cron.WithParser( @@ -184,15 +192,7 @@ func (c *command) runInForeground(profileCronExpression string) error { cs, err := sourceConfiguration(configStrategyConfd) if err != nil { - if !os.IsNotExist(err) { - return fmt.Errorf("runInForeground: could not load config from environment files: %w", err) - } - cs, err = sourceConfiguration(configStrategyEnv) - if err != nil { - return fmt.Errorf("runInForeground: could not load configuration: %w", err) - } - } else { - c.logger.Info("/etc/dockervolumebackup/conf.d was found, using configuration files from this directory.") + return fmt.Errorf("runInForeground: error sourcing configuration: %w", err) } for _, config := range cs { @@ -218,6 +218,8 @@ func (c *command) runInForeground(profileCronExpression string) error { return nil } +// runAsCommand executes a backup run for each configuration that is available +// and then returns func (c *command) runAsCommand() error { configurations, err := sourceConfiguration(configStrategyEnv) if err != nil {