diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 7d89e2dff..1b9c5b33d 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -535,112 +535,21 @@ func OpenRepository(ctx context.Context, opts GlobalOptions) (*repository.Reposi } func parseConfig(loc location.Location, opts options.Options) (interface{}, error) { - // only apply options for a particular backend here - opts = opts.Extract(loc.Scheme) - - switch loc.Scheme { - case "local": - cfg := loc.Config.(*local.Config) - if err := opts.Apply(loc.Scheme, cfg); err != nil { + cfg := loc.Config + if cfg, ok := cfg.(restic.ApplyEnvironmenter); ok { + if err := cfg.ApplyEnvironment(""); err != nil { return nil, err } - - debug.Log("opening local repository at %#v", cfg) - return cfg, nil - - case "sftp": - cfg := loc.Config.(*sftp.Config) - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening sftp repository at %#v", cfg) - return cfg, nil - - case "s3": - cfg := loc.Config.(*s3.Config) - - if err := s3.ApplyEnvironment(cfg); err != nil { - return nil, err - } - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening s3 repository at %#v", cfg) - return cfg, nil - - case "gs": - cfg := loc.Config.(*gs.Config) - - if err := gs.ApplyEnvironment(cfg); err != nil { - return nil, err - } - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening gs repository at %#v", cfg) - return cfg, nil - - case "azure": - cfg := loc.Config.(*azure.Config) - - if err := azure.ApplyEnvironment(cfg); err != nil { - return nil, err - } - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening gs repository at %#v", cfg) - return cfg, nil - - case "swift": - cfg := loc.Config.(*swift.Config) - - if err := swift.ApplyEnvironment("", cfg); err != nil { - return nil, err - } - - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening swift repository at %#v", cfg) - return cfg, nil - - case "b2": - cfg := loc.Config.(*b2.Config) - - if err := b2.ApplyEnvironment(cfg); err != nil { - return nil, err - } - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening b2 repository at %#v", cfg) - return cfg, nil - case "rest": - cfg := loc.Config.(*rest.Config) - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening rest repository at %#v", cfg) - return cfg, nil - case "rclone": - cfg := loc.Config.(*rclone.Config) - if err := opts.Apply(loc.Scheme, cfg); err != nil { - return nil, err - } - - debug.Log("opening rest repository at %#v", cfg) - return cfg, nil } - return nil, errors.Fatalf("invalid backend: %q", loc.Scheme) + // only apply options for a particular backend here + opts = opts.Extract(loc.Scheme) + if err := opts.Apply(loc.Scheme, cfg); err != nil { + return nil, err + } + + debug.Log("opening %v repository at %#v", loc.Scheme, cfg) + return cfg, nil } // Open the backend specified by a location config. diff --git a/internal/backend/azure/config.go b/internal/backend/azure/config.go index 9ba42d963..4d4e839ff 100644 --- a/internal/backend/azure/config.go +++ b/internal/backend/azure/config.go @@ -7,6 +7,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" + "github.com/restic/restic/internal/restic" ) // Config contains all configuration necessary to connect to an azure compatible @@ -55,19 +56,20 @@ func ParseConfig(s string) (*Config, error) { return &cfg, nil } +var _ restic.ApplyEnvironmenter = &Config{} + // ApplyEnvironment saves values from the environment to the config. -func ApplyEnvironment(cfgRaw interface{}) error { - cfg := cfgRaw.(*Config) +func (cfg *Config) ApplyEnvironment(prefix string) error { if cfg.AccountName == "" { - cfg.AccountName = os.Getenv("AZURE_ACCOUNT_NAME") + cfg.AccountName = os.Getenv(prefix + "AZURE_ACCOUNT_NAME") } if cfg.AccountKey.String() == "" { - cfg.AccountKey = options.NewSecretString(os.Getenv("AZURE_ACCOUNT_KEY")) + cfg.AccountKey = options.NewSecretString(os.Getenv(prefix + "AZURE_ACCOUNT_KEY")) } if cfg.AccountSAS.String() == "" { - cfg.AccountSAS = options.NewSecretString(os.Getenv("AZURE_ACCOUNT_SAS")) + cfg.AccountSAS = options.NewSecretString(os.Getenv(prefix + "AZURE_ACCOUNT_SAS")) } return nil } diff --git a/internal/backend/b2/config.go b/internal/backend/b2/config.go index 69b99a09b..548fbef99 100644 --- a/internal/backend/b2/config.go +++ b/internal/backend/b2/config.go @@ -8,6 +8,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" + "github.com/restic/restic/internal/restic" ) // Config contains all configuration necessary to connect to an b2 compatible @@ -81,11 +82,12 @@ func ParseConfig(s string) (*Config, error) { return &cfg, nil } +var _ restic.ApplyEnvironmenter = &Config{} + // ApplyEnvironment saves values from the environment to the config. -func ApplyEnvironment(cfgRaw interface{}) error { - cfg := cfgRaw.(*Config) +func (cfg *Config) ApplyEnvironment(prefix string) error { if cfg.AccountID == "" { - cfg.AccountID = os.Getenv("B2_ACCOUNT_ID") + cfg.AccountID = os.Getenv(prefix + "B2_ACCOUNT_ID") } if cfg.AccountID == "" { @@ -93,7 +95,7 @@ func ApplyEnvironment(cfgRaw interface{}) error { } if cfg.Key.String() == "" { - cfg.Key = options.NewSecretString(os.Getenv("B2_ACCOUNT_KEY")) + cfg.Key = options.NewSecretString(os.Getenv(prefix + "B2_ACCOUNT_KEY")) } if cfg.Key.String() == "" { diff --git a/internal/backend/gs/config.go b/internal/backend/gs/config.go index 4809cd15d..b2d52c5f8 100644 --- a/internal/backend/gs/config.go +++ b/internal/backend/gs/config.go @@ -7,6 +7,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" + "github.com/restic/restic/internal/restic" ) // Config contains all configuration necessary to connect to a Google Cloud Storage @@ -58,11 +59,12 @@ func ParseConfig(s string) (*Config, error) { return &cfg, nil } +var _ restic.ApplyEnvironmenter = &Config{} + // ApplyEnvironment saves values from the environment to the config. -func ApplyEnvironment(cfgRaw interface{}) error { - cfg := cfgRaw.(*Config) +func (cfg *Config) ApplyEnvironment(prefix string) error { if cfg.ProjectID == "" { - cfg.ProjectID = os.Getenv("GOOGLE_PROJECT_ID") + cfg.ProjectID = os.Getenv(prefix + "GOOGLE_PROJECT_ID") } return nil } diff --git a/internal/backend/s3/config.go b/internal/backend/s3/config.go index 74db58c8f..525373d16 100644 --- a/internal/backend/s3/config.go +++ b/internal/backend/s3/config.go @@ -8,6 +8,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" + "github.com/restic/restic/internal/restic" ) // Config contains all configuration necessary to connect to an s3 compatible @@ -93,15 +94,16 @@ func createConfig(endpoint, bucket, prefix string, useHTTP bool) (*Config, error return &cfg, nil } +var _ restic.ApplyEnvironmenter = &Config{} + // ApplyEnvironment saves values from the environment to the config. -func ApplyEnvironment(cfgRaw interface{}) error { - cfg := cfgRaw.(*Config) +func (cfg *Config) ApplyEnvironment(prefix string) error { if cfg.KeyID == "" { - cfg.KeyID = os.Getenv("AWS_ACCESS_KEY_ID") + cfg.KeyID = os.Getenv(prefix + "AWS_ACCESS_KEY_ID") } if cfg.Secret.String() == "" { - cfg.Secret = options.NewSecretString(os.Getenv("AWS_SECRET_ACCESS_KEY")) + cfg.Secret = options.NewSecretString(os.Getenv(prefix + "AWS_SECRET_ACCESS_KEY")) } if cfg.KeyID == "" && cfg.Secret.String() != "" { @@ -111,7 +113,7 @@ func ApplyEnvironment(cfgRaw interface{}) error { } if cfg.Region == "" { - cfg.Region = os.Getenv("AWS_DEFAULT_REGION") + cfg.Region = os.Getenv(prefix + "AWS_DEFAULT_REGION") } return nil diff --git a/internal/backend/swift/config.go b/internal/backend/swift/config.go index f6372fb30..b9f5d3995 100644 --- a/internal/backend/swift/config.go +++ b/internal/backend/swift/config.go @@ -6,6 +6,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/options" + "github.com/restic/restic/internal/restic" ) // Config contains basic configuration needed to specify swift location for a swift server @@ -73,45 +74,46 @@ func ParseConfig(s string) (*Config, error) { return &cfg, nil } +var _ restic.ApplyEnvironmenter = &Config{} + // ApplyEnvironment saves values from the environment to the config. -func ApplyEnvironment(prefix string, cfg interface{}) error { - c := cfg.(*Config) +func (cfg *Config) ApplyEnvironment(prefix string) error { for _, val := range []struct { s *string env string }{ // v2/v3 specific - {&c.UserName, prefix + "OS_USERNAME"}, - {&c.APIKey, prefix + "OS_PASSWORD"}, - {&c.Region, prefix + "OS_REGION_NAME"}, - {&c.AuthURL, prefix + "OS_AUTH_URL"}, + {&cfg.UserName, prefix + "OS_USERNAME"}, + {&cfg.APIKey, prefix + "OS_PASSWORD"}, + {&cfg.Region, prefix + "OS_REGION_NAME"}, + {&cfg.AuthURL, prefix + "OS_AUTH_URL"}, // v3 specific - {&c.UserID, prefix + "OS_USER_ID"}, - {&c.Domain, prefix + "OS_USER_DOMAIN_NAME"}, - {&c.DomainID, prefix + "OS_USER_DOMAIN_ID"}, - {&c.Tenant, prefix + "OS_PROJECT_NAME"}, - {&c.TenantDomain, prefix + "OS_PROJECT_DOMAIN_NAME"}, - {&c.TenantDomainID, prefix + "OS_PROJECT_DOMAIN_ID"}, - {&c.TrustID, prefix + "OS_TRUST_ID"}, + {&cfg.UserID, prefix + "OS_USER_ID"}, + {&cfg.Domain, prefix + "OS_USER_DOMAIN_NAME"}, + {&cfg.DomainID, prefix + "OS_USER_DOMAIN_ID"}, + {&cfg.Tenant, prefix + "OS_PROJECT_NAME"}, + {&cfg.TenantDomain, prefix + "OS_PROJECT_DOMAIN_NAME"}, + {&cfg.TenantDomainID, prefix + "OS_PROJECT_DOMAIN_ID"}, + {&cfg.TrustID, prefix + "OS_TRUST_ID"}, // v2 specific - {&c.TenantID, prefix + "OS_TENANT_ID"}, - {&c.Tenant, prefix + "OS_TENANT_NAME"}, + {&cfg.TenantID, prefix + "OS_TENANT_ID"}, + {&cfg.Tenant, prefix + "OS_TENANT_NAME"}, // v1 specific - {&c.AuthURL, prefix + "ST_AUTH"}, - {&c.UserName, prefix + "ST_USER"}, - {&c.APIKey, prefix + "ST_KEY"}, + {&cfg.AuthURL, prefix + "ST_AUTH"}, + {&cfg.UserName, prefix + "ST_USER"}, + {&cfg.APIKey, prefix + "ST_KEY"}, // Application Credential auth - {&c.ApplicationCredentialID, prefix + "OS_APPLICATION_CREDENTIAL_ID"}, - {&c.ApplicationCredentialName, prefix + "OS_APPLICATION_CREDENTIAL_NAME"}, + {&cfg.ApplicationCredentialID, prefix + "OS_APPLICATION_CREDENTIAL_ID"}, + {&cfg.ApplicationCredentialName, prefix + "OS_APPLICATION_CREDENTIAL_NAME"}, // Manual authentication - {&c.StorageURL, prefix + "OS_STORAGE_URL"}, + {&cfg.StorageURL, prefix + "OS_STORAGE_URL"}, - {&c.DefaultContainerPolicy, prefix + "SWIFT_DEFAULT_CONTAINER_POLICY"}, + {&cfg.DefaultContainerPolicy, prefix + "SWIFT_DEFAULT_CONTAINER_POLICY"}, } { if *val.s == "" { *val.s = os.Getenv(val.env) @@ -121,8 +123,8 @@ func ApplyEnvironment(prefix string, cfg interface{}) error { s *options.SecretString env string }{ - {&c.ApplicationCredentialSecret, prefix + "OS_APPLICATION_CREDENTIAL_SECRET"}, - {&c.AuthToken, prefix + "OS_AUTH_TOKEN"}, + {&cfg.ApplicationCredentialSecret, prefix + "OS_APPLICATION_CREDENTIAL_SECRET"}, + {&cfg.AuthToken, prefix + "OS_AUTH_TOKEN"}, } { if val.s.String() == "" { *val.s = options.NewSecretString(os.Getenv(val.env)) diff --git a/internal/backend/swift/swift_test.go b/internal/backend/swift/swift_test.go index ceb823a53..cb0992010 100644 --- a/internal/backend/swift/swift_test.go +++ b/internal/backend/swift/swift_test.go @@ -48,7 +48,7 @@ func newSwiftTestSuite(t testing.TB) *test.Suite[swift.Config] { return nil, err } - if err = swift.ApplyEnvironment("RESTIC_TEST_", &cfg); err != nil { + if err = cfg.ApplyEnvironment("RESTIC_TEST_"); err != nil { return nil, err } cfg.Prefix += fmt.Sprintf("/test-%d", time.Now().UnixNano()) diff --git a/internal/restic/backend.go b/internal/restic/backend.go index b01071132..b6653fcb4 100644 --- a/internal/restic/backend.go +++ b/internal/restic/backend.go @@ -80,3 +80,8 @@ type FileInfo struct { Size int64 Name string } + +// ApplyEnvironmenter fills in a backend configuration from the environment +type ApplyEnvironmenter interface { + ApplyEnvironment(prefix string) error +}