From f903db492c80f0211ffd89e186e9df2dba142ec3 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Fri, 21 Apr 2023 21:35:34 +0200 Subject: [PATCH] backend: let ParseConfig return a Config pointer In order to change the backend initialization in `global.go` to be able to generically call cfg.ApplyEnvironment() for supported backends, the `interface{}` returned by `ParseConfig` must contain a pointer to the configuration. An alternative would be to use reflection to convert the type from `interface{}(Config)` to `interface{}(*Config)` (from value to pointer type). However, this would just complicate the type mess further. --- cmd/restic/global.go | 82 +++++++++++----------- internal/backend/azure/azure_test.go | 6 +- internal/backend/azure/config.go | 8 +-- internal/backend/b2/b2_test.go | 4 +- internal/backend/b2/config.go | 8 +-- internal/backend/gs/config.go | 8 +-- internal/backend/gs/gs_test.go | 4 +- internal/backend/local/config.go | 6 +- internal/backend/local/config_test.go | 18 +++++ internal/backend/local/local_test.go | 4 +- internal/backend/location/location.go | 2 +- internal/backend/location/location_test.go | 58 +++++++-------- internal/backend/mem/mem_backend_test.go | 5 +- internal/backend/rclone/backend_test.go | 4 +- internal/backend/rclone/config.go | 6 +- internal/backend/rest/config.go | 8 +-- internal/backend/rest/rest_test.go | 4 +- internal/backend/s3/config.go | 14 ++-- internal/backend/s3/s3_test.go | 8 +-- internal/backend/sftp/config.go | 14 ++-- internal/backend/sftp/sftp_test.go | 4 +- internal/backend/swift/config.go | 10 +-- internal/backend/swift/swift_test.go | 6 +- internal/backend/test/config.go | 6 +- internal/backend/test/suite.go | 12 ++-- internal/backend/test/tests.go | 2 +- 26 files changed, 165 insertions(+), 146 deletions(-) create mode 100644 internal/backend/local/config_test.go diff --git a/cmd/restic/global.go b/cmd/restic/global.go index bb0f8a570..7d89e2dff 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -540,8 +540,8 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro switch loc.Scheme { case "local": - cfg := loc.Config.(local.Config) - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + cfg := loc.Config.(*local.Config) + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -549,8 +549,8 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "sftp": - cfg := loc.Config.(sftp.Config) - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + cfg := loc.Config.(*sftp.Config) + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -558,12 +558,12 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "s3": - cfg := loc.Config.(s3.Config) + cfg := loc.Config.(*s3.Config) - if err := s3.ApplyEnvironment(&cfg); err != nil { + if err := s3.ApplyEnvironment(cfg); err != nil { return nil, err } - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -571,12 +571,12 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "gs": - cfg := loc.Config.(gs.Config) + cfg := loc.Config.(*gs.Config) - if err := gs.ApplyEnvironment(&cfg); err != nil { + if err := gs.ApplyEnvironment(cfg); err != nil { return nil, err } - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -584,12 +584,12 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "azure": - cfg := loc.Config.(azure.Config) + cfg := loc.Config.(*azure.Config) - if err := azure.ApplyEnvironment(&cfg); err != nil { + if err := azure.ApplyEnvironment(cfg); err != nil { return nil, err } - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -597,13 +597,13 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "swift": - cfg := loc.Config.(swift.Config) + cfg := loc.Config.(*swift.Config) - if err := swift.ApplyEnvironment("", &cfg); err != nil { + if err := swift.ApplyEnvironment("", cfg); err != nil { return nil, err } - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -611,28 +611,28 @@ func parseConfig(loc location.Location, opts options.Options) (interface{}, erro return cfg, nil case "b2": - cfg := loc.Config.(b2.Config) + cfg := loc.Config.(*b2.Config) - if err := b2.ApplyEnvironment(&cfg); err != nil { + if err := b2.ApplyEnvironment(cfg); err != nil { return nil, err } - if err := opts.Apply(loc.Scheme, &cfg); err != nil { + 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 { + 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 { + cfg := loc.Config.(*rclone.Config) + if err := opts.Apply(loc.Scheme, cfg); err != nil { return nil, err } @@ -669,23 +669,23 @@ func open(ctx context.Context, s string, gopts GlobalOptions, opts options.Optio switch loc.Scheme { case "local": - be, err = local.Open(ctx, cfg.(local.Config)) + be, err = local.Open(ctx, *cfg.(*local.Config)) case "sftp": - be, err = sftp.Open(ctx, cfg.(sftp.Config)) + be, err = sftp.Open(ctx, *cfg.(*sftp.Config)) case "s3": - be, err = s3.Open(ctx, cfg.(s3.Config), rt) + be, err = s3.Open(ctx, *cfg.(*s3.Config), rt) case "gs": - be, err = gs.Open(cfg.(gs.Config), rt) + be, err = gs.Open(*cfg.(*gs.Config), rt) case "azure": - be, err = azure.Open(ctx, cfg.(azure.Config), rt) + be, err = azure.Open(ctx, *cfg.(*azure.Config), rt) case "swift": - be, err = swift.Open(ctx, cfg.(swift.Config), rt) + be, err = swift.Open(ctx, *cfg.(*swift.Config), rt) case "b2": - be, err = b2.Open(ctx, cfg.(b2.Config), rt) + be, err = b2.Open(ctx, *cfg.(*b2.Config), rt) case "rest": - be, err = rest.Open(cfg.(rest.Config), rt) + be, err = rest.Open(*cfg.(*rest.Config), rt) case "rclone": - be, err = rclone.Open(cfg.(rclone.Config), lim) + be, err = rclone.Open(*cfg.(*rclone.Config), lim) default: return nil, errors.Fatalf("invalid backend: %q", loc.Scheme) @@ -745,23 +745,23 @@ func create(ctx context.Context, s string, opts options.Options) (restic.Backend var be restic.Backend switch loc.Scheme { case "local": - be, err = local.Create(ctx, cfg.(local.Config)) + be, err = local.Create(ctx, *cfg.(*local.Config)) case "sftp": - be, err = sftp.Create(ctx, cfg.(sftp.Config)) + be, err = sftp.Create(ctx, *cfg.(*sftp.Config)) case "s3": - be, err = s3.Create(ctx, cfg.(s3.Config), rt) + be, err = s3.Create(ctx, *cfg.(*s3.Config), rt) case "gs": - be, err = gs.Create(ctx, cfg.(gs.Config), rt) + be, err = gs.Create(ctx, *cfg.(*gs.Config), rt) case "azure": - be, err = azure.Create(ctx, cfg.(azure.Config), rt) + be, err = azure.Create(ctx, *cfg.(*azure.Config), rt) case "swift": - be, err = swift.Open(ctx, cfg.(swift.Config), rt) + be, err = swift.Open(ctx, *cfg.(*swift.Config), rt) case "b2": - be, err = b2.Create(ctx, cfg.(b2.Config), rt) + be, err = b2.Create(ctx, *cfg.(*b2.Config), rt) case "rest": - be, err = rest.Create(ctx, cfg.(rest.Config), rt) + be, err = rest.Create(ctx, *cfg.(*rest.Config), rt) case "rclone": - be, err = rclone.Create(ctx, cfg.(rclone.Config)) + be, err = rclone.Create(ctx, *cfg.(*rclone.Config)) default: debug.Log("invalid repository scheme: %v", s) return nil, errors.Fatalf("invalid scheme %q", loc.Scheme) diff --git a/internal/backend/azure/azure_test.go b/internal/backend/azure/azure_test.go index f7492ae1b..9185406f8 100644 --- a/internal/backend/azure/azure_test.go +++ b/internal/backend/azure/azure_test.go @@ -29,10 +29,10 @@ func newAzureTestSuite(t testing.TB) *test.Suite[azure.Config] { MinimalData: true, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (azure.Config, error) { + NewConfig: func() (*azure.Config, error) { cfg, err := azure.ParseConfig(os.Getenv("RESTIC_TEST_AZURE_REPOSITORY")) if err != nil { - return azure.Config{}, err + return nil, err } cfg.AccountName = os.Getenv("RESTIC_TEST_AZURE_ACCOUNT_NAME") @@ -150,7 +150,7 @@ func TestUploadLargeFile(t *testing.T) { t.Fatal(err) } - be, err := azure.Create(ctx, cfg, tr) + be, err := azure.Create(ctx, *cfg, tr) if err != nil { t.Fatal(err) } diff --git a/internal/backend/azure/config.go b/internal/backend/azure/config.go index adbc93430..9ba42d963 100644 --- a/internal/backend/azure/config.go +++ b/internal/backend/azure/config.go @@ -34,9 +34,9 @@ func init() { // ParseConfig parses the string s and extracts the azure config. The // configuration format is azure:containerName:/[prefix]. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "azure:") { - return Config{}, errors.New("azure: invalid format") + return nil, errors.New("azure: invalid format") } // strip prefix "azure:" @@ -46,13 +46,13 @@ func ParseConfig(s string) (Config, error) { // remainder as prefix container, prefix, colon := strings.Cut(s, ":") if !colon { - return Config{}, errors.New("azure: invalid format: bucket name or path not found") + return nil, errors.New("azure: invalid format: bucket name or path not found") } prefix = strings.TrimPrefix(path.Clean(prefix), "/") cfg := NewConfig() cfg.Container = container cfg.Prefix = prefix - return cfg, nil + return &cfg, nil } // ApplyEnvironment saves values from the environment to the config. diff --git a/internal/backend/b2/b2_test.go b/internal/backend/b2/b2_test.go index f634852d0..312f9640d 100644 --- a/internal/backend/b2/b2_test.go +++ b/internal/backend/b2/b2_test.go @@ -30,10 +30,10 @@ func newB2TestSuite(t testing.TB) *test.Suite[b2.Config] { WaitForDelayedRemoval: 10 * time.Second, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (b2.Config, error) { + NewConfig: func() (*b2.Config, error) { cfg, err := b2.ParseConfig(os.Getenv("RESTIC_TEST_B2_REPOSITORY")) if err != nil { - return b2.Config{}, err + return nil, err } cfg.AccountID = os.Getenv("RESTIC_TEST_B2_ACCOUNT_ID") diff --git a/internal/backend/b2/config.go b/internal/backend/b2/config.go index 6244c1799..69b99a09b 100644 --- a/internal/backend/b2/config.go +++ b/internal/backend/b2/config.go @@ -59,15 +59,15 @@ func checkBucketName(name string) error { // ParseConfig parses the string s and extracts the b2 config. The supported // configuration format is b2:bucketname/prefix. If no prefix is given the // prefix "restic" will be used. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "b2:") { - return Config{}, errors.New("invalid format, want: b2:bucket-name[:path]") + return nil, errors.New("invalid format, want: b2:bucket-name[:path]") } s = s[3:] bucket, prefix, _ := strings.Cut(s, ":") if err := checkBucketName(bucket); err != nil { - return Config{}, err + return nil, err } if len(prefix) > 0 { @@ -78,7 +78,7 @@ func ParseConfig(s string) (Config, error) { cfg.Bucket = bucket cfg.Prefix = prefix - return cfg, nil + return &cfg, nil } // ApplyEnvironment saves values from the environment to the config. diff --git a/internal/backend/gs/config.go b/internal/backend/gs/config.go index 204d568e4..4809cd15d 100644 --- a/internal/backend/gs/config.go +++ b/internal/backend/gs/config.go @@ -35,9 +35,9 @@ func init() { // ParseConfig parses the string s and extracts the gcs config. The // supported configuration format is gs:bucketName:/[prefix]. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "gs:") { - return Config{}, errors.New("gs: invalid format") + return nil, errors.New("gs: invalid format") } // strip prefix "gs:" @@ -47,7 +47,7 @@ func ParseConfig(s string) (Config, error) { // remainder as prefix bucket, prefix, colon := strings.Cut(s, ":") if !colon { - return Config{}, errors.New("gs: invalid format: bucket name or path not found") + return nil, errors.New("gs: invalid format: bucket name or path not found") } prefix = strings.TrimPrefix(path.Clean(prefix), "/") @@ -55,7 +55,7 @@ func ParseConfig(s string) (Config, error) { cfg := NewConfig() cfg.Bucket = bucket cfg.Prefix = prefix - return cfg, nil + return &cfg, nil } // ApplyEnvironment saves values from the environment to the config. diff --git a/internal/backend/gs/gs_test.go b/internal/backend/gs/gs_test.go index b9015e24c..f96b6c62b 100644 --- a/internal/backend/gs/gs_test.go +++ b/internal/backend/gs/gs_test.go @@ -26,10 +26,10 @@ func newGSTestSuite(t testing.TB) *test.Suite[gs.Config] { MinimalData: true, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (gs.Config, error) { + NewConfig: func() (*gs.Config, error) { cfg, err := gs.ParseConfig(os.Getenv("RESTIC_TEST_GS_REPOSITORY")) if err != nil { - return gs.Config{}, err + return nil, err } cfg.ProjectID = os.Getenv("RESTIC_TEST_GS_PROJECT_ID") diff --git a/internal/backend/local/config.go b/internal/backend/local/config.go index c68ae7765..dc5e7948c 100644 --- a/internal/backend/local/config.go +++ b/internal/backend/local/config.go @@ -27,12 +27,12 @@ func init() { } // ParseConfig parses a local backend config. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "local:") { - return Config{}, errors.New(`invalid format, prefix "local" not found`) + return nil, errors.New(`invalid format, prefix "local" not found`) } cfg := NewConfig() cfg.Path = s[6:] - return cfg, nil + return &cfg, nil } diff --git a/internal/backend/local/config_test.go b/internal/backend/local/config_test.go new file mode 100644 index 000000000..c9b6be61c --- /dev/null +++ b/internal/backend/local/config_test.go @@ -0,0 +1,18 @@ +package local + +import ( + "testing" + + "github.com/restic/restic/internal/backend/test" +) + +var configTests = []test.ConfigTestData[Config]{ + {S: "local:/some/path", Cfg: Config{ + Path: "/some/path", + Connections: 2, + }}, +} + +func TestParseConfig(t *testing.T) { + test.ParseConfigTester(t, ParseConfig, configTests) +} diff --git a/internal/backend/local/local_test.go b/internal/backend/local/local_test.go index 98afc5963..ca9e3b71b 100644 --- a/internal/backend/local/local_test.go +++ b/internal/backend/local/local_test.go @@ -15,7 +15,7 @@ import ( func newTestSuite(t testing.TB) *test.Suite[local.Config] { return &test.Suite[local.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (local.Config, error) { + NewConfig: func() (*local.Config, error) { dir, err := os.MkdirTemp(rtest.TestTempDir, "restic-test-local-") if err != nil { t.Fatal(err) @@ -23,7 +23,7 @@ func newTestSuite(t testing.TB) *test.Suite[local.Config] { t.Logf("create new backend at %v", dir) - cfg := local.Config{ + cfg := &local.Config{ Path: dir, Connections: 2, } diff --git a/internal/backend/location/location.go b/internal/backend/location/location.go index 6b7683be5..612ae1b4c 100644 --- a/internal/backend/location/location.go +++ b/internal/backend/location/location.go @@ -29,7 +29,7 @@ type parser struct { stripPassword func(string) string } -func configToAny[C any](parser func(string) (C, error)) func(string) (interface{}, error) { +func configToAny[C any](parser func(string) (*C, error)) func(string) (interface{}, error) { return func(s string) (interface{}, error) { return parser(s) } diff --git a/internal/backend/location/location_test.go b/internal/backend/location/location_test.go index 809379850..9f5db70c9 100644 --- a/internal/backend/location/location_test.go +++ b/internal/backend/location/location_test.go @@ -29,7 +29,7 @@ var parseTests = []struct { { "local:/srv/repo", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "/srv/repo", Connections: 2, }, @@ -38,7 +38,7 @@ var parseTests = []struct { { "local:dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "dir1/dir2", Connections: 2, }, @@ -47,7 +47,7 @@ var parseTests = []struct { { "local:dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "dir1/dir2", Connections: 2, }, @@ -56,7 +56,7 @@ var parseTests = []struct { { "dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "dir1/dir2", Connections: 2, }, @@ -65,7 +65,7 @@ var parseTests = []struct { { "/dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "/dir1/dir2", Connections: 2, }, @@ -74,7 +74,7 @@ var parseTests = []struct { { "local:../dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "../dir1/dir2", Connections: 2, }, @@ -83,7 +83,7 @@ var parseTests = []struct { { "/dir1/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "/dir1/dir2", Connections: 2, }, @@ -92,7 +92,7 @@ var parseTests = []struct { { "/dir1:foobar/dir2", Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: "/dir1:foobar/dir2", Connections: 2, }, @@ -101,7 +101,7 @@ var parseTests = []struct { { `\dir1\foobar\dir2`, Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: `\dir1\foobar\dir2`, Connections: 2, }, @@ -110,7 +110,7 @@ var parseTests = []struct { { `c:\dir1\foobar\dir2`, Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: `c:\dir1\foobar\dir2`, Connections: 2, }, @@ -119,7 +119,7 @@ var parseTests = []struct { { `C:\Users\appveyor\AppData\Local\Temp\1\restic-test-879453535\repo`, Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: `C:\Users\appveyor\AppData\Local\Temp\1\restic-test-879453535\repo`, Connections: 2, }, @@ -128,7 +128,7 @@ var parseTests = []struct { { `c:/dir1/foobar/dir2`, Location{Scheme: "local", - Config: local.Config{ + Config: &local.Config{ Path: `c:/dir1/foobar/dir2`, Connections: 2, }, @@ -137,7 +137,7 @@ var parseTests = []struct { { "sftp:user@host:/srv/repo", Location{Scheme: "sftp", - Config: sftp.Config{ + Config: &sftp.Config{ User: "user", Host: "host", Path: "/srv/repo", @@ -148,7 +148,7 @@ var parseTests = []struct { { "sftp:host:/srv/repo", Location{Scheme: "sftp", - Config: sftp.Config{ + Config: &sftp.Config{ User: "", Host: "host", Path: "/srv/repo", @@ -159,7 +159,7 @@ var parseTests = []struct { { "sftp://user@host/srv/repo", Location{Scheme: "sftp", - Config: sftp.Config{ + Config: &sftp.Config{ User: "user", Host: "host", Path: "srv/repo", @@ -170,7 +170,7 @@ var parseTests = []struct { { "sftp://user@host//srv/repo", Location{Scheme: "sftp", - Config: sftp.Config{ + Config: &sftp.Config{ User: "user", Host: "host", Path: "/srv/repo", @@ -182,7 +182,7 @@ var parseTests = []struct { { "s3://eu-central-1/bucketname", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "eu-central-1", Bucket: "bucketname", Prefix: "", @@ -193,7 +193,7 @@ var parseTests = []struct { { "s3://hostname.foo/bucketname", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "hostname.foo", Bucket: "bucketname", Prefix: "", @@ -204,7 +204,7 @@ var parseTests = []struct { { "s3://hostname.foo/bucketname/prefix/directory", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "hostname.foo", Bucket: "bucketname", Prefix: "prefix/directory", @@ -215,7 +215,7 @@ var parseTests = []struct { { "s3:eu-central-1/repo", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "eu-central-1", Bucket: "repo", Prefix: "", @@ -226,7 +226,7 @@ var parseTests = []struct { { "s3:eu-central-1/repo/prefix/directory", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "eu-central-1", Bucket: "repo", Prefix: "prefix/directory", @@ -237,7 +237,7 @@ var parseTests = []struct { { "s3:https://hostname.foo/repo", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "hostname.foo", Bucket: "repo", Prefix: "", @@ -248,7 +248,7 @@ var parseTests = []struct { { "s3:https://hostname.foo/repo/prefix/directory", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "hostname.foo", Bucket: "repo", Prefix: "prefix/directory", @@ -259,7 +259,7 @@ var parseTests = []struct { { "s3:http://hostname.foo/repo", Location{Scheme: "s3", - Config: s3.Config{ + Config: &s3.Config{ Endpoint: "hostname.foo", Bucket: "repo", Prefix: "", @@ -271,7 +271,7 @@ var parseTests = []struct { { "swift:container17:/", Location{Scheme: "swift", - Config: swift.Config{ + Config: &swift.Config{ Container: "container17", Prefix: "", Connections: 5, @@ -281,7 +281,7 @@ var parseTests = []struct { { "swift:container17:/prefix97", Location{Scheme: "swift", - Config: swift.Config{ + Config: &swift.Config{ Container: "container17", Prefix: "prefix97", Connections: 5, @@ -291,7 +291,7 @@ var parseTests = []struct { { "rest:http://hostname.foo:1234/", Location{Scheme: "rest", - Config: rest.Config{ + Config: &rest.Config{ URL: parseURL("http://hostname.foo:1234/"), Connections: 5, }, @@ -299,7 +299,7 @@ var parseTests = []struct { }, { "b2:bucketname:/prefix", Location{Scheme: "b2", - Config: b2.Config{ + Config: &b2.Config{ Bucket: "bucketname", Prefix: "prefix", Connections: 5, @@ -308,7 +308,7 @@ var parseTests = []struct { }, { "b2:bucketname", Location{Scheme: "b2", - Config: b2.Config{ + Config: &b2.Config{ Bucket: "bucketname", Prefix: "", Connections: 5, diff --git a/internal/backend/mem/mem_backend_test.go b/internal/backend/mem/mem_backend_test.go index 7b862e314..3dea089bc 100644 --- a/internal/backend/mem/mem_backend_test.go +++ b/internal/backend/mem/mem_backend_test.go @@ -18,8 +18,9 @@ type memConfig struct { func newTestSuite() *test.Suite[*memConfig] { return &test.Suite[*memConfig]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (*memConfig, error) { - return &memConfig{}, nil + NewConfig: func() (**memConfig, error) { + cfg := &memConfig{} + return &cfg, nil }, // CreateFn is a function that creates a temporary repository for the tests. diff --git a/internal/backend/rclone/backend_test.go b/internal/backend/rclone/backend_test.go index 4bff46d77..c497271f6 100644 --- a/internal/backend/rclone/backend_test.go +++ b/internal/backend/rclone/backend_test.go @@ -17,11 +17,11 @@ func newTestSuite(t testing.TB) *test.Suite[rclone.Config] { return &test.Suite[rclone.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (rclone.Config, error) { + NewConfig: func() (*rclone.Config, error) { t.Logf("use backend at %v", dir) cfg := rclone.NewConfig() cfg.Remote = dir - return cfg, nil + return &cfg, nil }, // CreateFn is a function that creates a temporary repository for the tests. diff --git a/internal/backend/rclone/config.go b/internal/backend/rclone/config.go index 0f1eef332..2071d84e2 100644 --- a/internal/backend/rclone/config.go +++ b/internal/backend/rclone/config.go @@ -34,13 +34,13 @@ func NewConfig() Config { } // ParseConfig parses the string s and extracts the remote server URL. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "rclone:") { - return Config{}, errors.New("invalid rclone backend specification") + return nil, errors.New("invalid rclone backend specification") } s = s[7:] cfg := NewConfig() cfg.Remote = s - return cfg, nil + return &cfg, nil } diff --git a/internal/backend/rest/config.go b/internal/backend/rest/config.go index b0b2b93f8..ba42a0220 100644 --- a/internal/backend/rest/config.go +++ b/internal/backend/rest/config.go @@ -26,21 +26,21 @@ func NewConfig() Config { } // ParseConfig parses the string s and extracts the REST server URL. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "rest:") { - return Config{}, errors.New("invalid REST backend specification") + return nil, errors.New("invalid REST backend specification") } s = prepareURL(s) u, err := url.Parse(s) if err != nil { - return Config{}, errors.WithStack(err) + return nil, errors.WithStack(err) } cfg := NewConfig() cfg.URL = u - return cfg, nil + return &cfg, nil } // StripPassword removes the password from the URL diff --git a/internal/backend/rest/rest_test.go b/internal/backend/rest/rest_test.go index 10e7b5b5b..2ebd00f5e 100644 --- a/internal/backend/rest/rest_test.go +++ b/internal/backend/rest/rest_test.go @@ -77,10 +77,10 @@ func newTestSuite(_ context.Context, t testing.TB, url *url.URL, minimalData boo MinimalData: minimalData, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (rest.Config, error) { + NewConfig: func() (*rest.Config, error) { cfg := rest.NewConfig() cfg.URL = url - return cfg, nil + return &cfg, nil }, // CreateFn is a function that creates a temporary repository for the tests. diff --git a/internal/backend/s3/config.go b/internal/backend/s3/config.go index 76e9f526b..74db58c8f 100644 --- a/internal/backend/s3/config.go +++ b/internal/backend/s3/config.go @@ -45,7 +45,7 @@ func init() { // supported configuration formats are s3://host/bucketname/prefix and // s3:host/bucketname/prefix. The host can also be a valid s3 region // name. If no prefix is given the prefix "restic" will be used. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { switch { case strings.HasPrefix(s, "s3:http"): // assume that a URL has been specified, parse it and @@ -53,11 +53,11 @@ func ParseConfig(s string) (Config, error) { // bucket name and prefix url, err := url.Parse(s[3:]) if err != nil { - return Config{}, errors.WithStack(err) + return nil, errors.WithStack(err) } if url.Path == "" { - return Config{}, errors.New("s3: bucket name not found") + return nil, errors.New("s3: bucket name not found") } bucket, path, _ := strings.Cut(url.Path[1:], "/") @@ -67,7 +67,7 @@ func ParseConfig(s string) (Config, error) { case strings.HasPrefix(s, "s3:"): s = s[3:] default: - return Config{}, errors.New("s3: invalid format") + return nil, errors.New("s3: invalid format") } // use the first entry of the path as the endpoint and the // remainder as bucket name and prefix @@ -76,9 +76,9 @@ func ParseConfig(s string) (Config, error) { return createConfig(endpoint, bucket, prefix, false) } -func createConfig(endpoint, bucket, prefix string, useHTTP bool) (Config, error) { +func createConfig(endpoint, bucket, prefix string, useHTTP bool) (*Config, error) { if endpoint == "" { - return Config{}, errors.New("s3: invalid format, host/region or bucket name not found") + return nil, errors.New("s3: invalid format, host/region or bucket name not found") } if prefix != "" { @@ -90,7 +90,7 @@ func createConfig(endpoint, bucket, prefix string, useHTTP bool) (Config, error) cfg.UseHTTP = useHTTP cfg.Bucket = bucket cfg.Prefix = prefix - return cfg, nil + return &cfg, nil } // ApplyEnvironment saves values from the environment to the config. diff --git a/internal/backend/s3/s3_test.go b/internal/backend/s3/s3_test.go index 5c91e9f7f..1cdc6d7e9 100644 --- a/internal/backend/s3/s3_test.go +++ b/internal/backend/s3/s3_test.go @@ -128,7 +128,7 @@ func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite[MinioTestC return &test.Suite[MinioTestConfig]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (MinioTestConfig, error) { + NewConfig: func() (*MinioTestConfig, error) { cfg := MinioTestConfig{} cfg.tempdir = rtest.TempDir(t) @@ -142,7 +142,7 @@ func newMinioTestSuite(ctx context.Context, t testing.TB) *test.Suite[MinioTestC cfg.Config.UseHTTP = true cfg.Config.KeyID = key cfg.Config.Secret = options.NewSecretString(secret) - return cfg, nil + return &cfg, nil }, // CreateFn is a function that creates a temporary repository for the tests. @@ -224,10 +224,10 @@ func newS3TestSuite(t testing.TB) *test.Suite[s3.Config] { MinimalData: true, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (s3.Config, error) { + NewConfig: func() (*s3.Config, error) { cfg, err := s3.ParseConfig(os.Getenv("RESTIC_TEST_S3_REPOSITORY")) if err != nil { - return s3.Config{}, err + return nil, err } cfg.KeyID = os.Getenv("RESTIC_TEST_S3_KEY") diff --git a/internal/backend/sftp/config.go b/internal/backend/sftp/config.go index b07aa0c20..ed7c2cafa 100644 --- a/internal/backend/sftp/config.go +++ b/internal/backend/sftp/config.go @@ -35,14 +35,14 @@ func init() { // and sftp:user@host:directory. The directory will be path Cleaned and can // be an absolute path if it starts with a '/' (e.g. // sftp://user@host//absolute and sftp:user@host:/absolute). -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { var user, host, port, dir string switch { case strings.HasPrefix(s, "sftp://"): // parse the "sftp://user@host/path" url format url, err := url.Parse(s) if err != nil { - return Config{}, errors.WithStack(err) + return nil, errors.WithStack(err) } if url.User != nil { user = url.User.Username() @@ -51,7 +51,7 @@ func ParseConfig(s string) (Config, error) { port = url.Port() dir = url.Path if dir == "" { - return Config{}, errors.Errorf("invalid backend %q, no directory specified", s) + return nil, errors.Errorf("invalid backend %q, no directory specified", s) } dir = dir[1:] @@ -63,7 +63,7 @@ func ParseConfig(s string) (Config, error) { var colon bool host, dir, colon = strings.Cut(s, ":") if !colon { - return Config{}, errors.New("sftp: invalid format, hostname or path not found") + return nil, errors.New("sftp: invalid format, hostname or path not found") } // split user and host at the "@" data := strings.SplitN(host, "@", 3) @@ -75,12 +75,12 @@ func ParseConfig(s string) (Config, error) { host = data[1] } default: - return Config{}, errors.New(`invalid format, does not start with "sftp:"`) + return nil, errors.New(`invalid format, does not start with "sftp:"`) } p := path.Clean(dir) if strings.HasPrefix(p, "~") { - return Config{}, errors.New("sftp path starts with the tilde (~) character, that fails for most sftp servers.\nUse a relative directory, most servers interpret this as relative to the user's home directory") + return nil, errors.New("sftp path starts with the tilde (~) character, that fails for most sftp servers.\nUse a relative directory, most servers interpret this as relative to the user's home directory") } cfg := NewConfig() @@ -89,5 +89,5 @@ func ParseConfig(s string) (Config, error) { cfg.Port = port cfg.Path = p - return cfg, nil + return &cfg, nil } diff --git a/internal/backend/sftp/sftp_test.go b/internal/backend/sftp/sftp_test.go index 34ad06554..98175ca26 100644 --- a/internal/backend/sftp/sftp_test.go +++ b/internal/backend/sftp/sftp_test.go @@ -32,7 +32,7 @@ var sftpServer = findSFTPServerBinary() func newTestSuite(t testing.TB) *test.Suite[sftp.Config] { return &test.Suite[sftp.Config]{ // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (sftp.Config, error) { + NewConfig: func() (*sftp.Config, error) { dir, err := os.MkdirTemp(rtest.TestTempDir, "restic-test-sftp-") if err != nil { t.Fatal(err) @@ -40,7 +40,7 @@ func newTestSuite(t testing.TB) *test.Suite[sftp.Config] { t.Logf("create new backend at %v", dir) - cfg := sftp.Config{ + cfg := &sftp.Config{ Path: dir, Command: fmt.Sprintf("%q -e", sftpServer), Connections: 5, diff --git a/internal/backend/swift/config.go b/internal/backend/swift/config.go index 2b4daeff0..f6372fb30 100644 --- a/internal/backend/swift/config.go +++ b/internal/backend/swift/config.go @@ -50,19 +50,19 @@ func NewConfig() Config { } // ParseConfig parses the string s and extract swift's container name and prefix. -func ParseConfig(s string) (Config, error) { +func ParseConfig(s string) (*Config, error) { if !strings.HasPrefix(s, "swift:") { - return Config{}, errors.New("invalid URL, expected: swift:container-name:/[prefix]") + return nil, errors.New("invalid URL, expected: swift:container-name:/[prefix]") } s = strings.TrimPrefix(s, "swift:") container, prefix, _ := strings.Cut(s, ":") if prefix == "" { - return Config{}, errors.Errorf("prefix is empty") + return nil, errors.Errorf("prefix is empty") } if prefix[0] != '/' { - return Config{}, errors.Errorf("prefix does not start with slash (/)") + return nil, errors.Errorf("prefix does not start with slash (/)") } prefix = prefix[1:] @@ -70,7 +70,7 @@ func ParseConfig(s string) (Config, error) { cfg.Container = container cfg.Prefix = prefix - return cfg, nil + return &cfg, nil } // ApplyEnvironment saves values from the environment to the config. diff --git a/internal/backend/swift/swift_test.go b/internal/backend/swift/swift_test.go index 316028b0c..ceb823a53 100644 --- a/internal/backend/swift/swift_test.go +++ b/internal/backend/swift/swift_test.go @@ -42,14 +42,14 @@ func newSwiftTestSuite(t testing.TB) *test.Suite[swift.Config] { }, // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig: func() (swift.Config, error) { + NewConfig: func() (*swift.Config, error) { cfg, err := swift.ParseConfig(os.Getenv("RESTIC_TEST_SWIFT")) if err != nil { - return swift.Config{}, err + return nil, err } if err = swift.ApplyEnvironment("RESTIC_TEST_", &cfg); err != nil { - return swift.Config{}, err + return nil, err } cfg.Prefix += fmt.Sprintf("/test-%d", time.Now().UnixNano()) t.Logf("using prefix %v", cfg.Prefix) diff --git a/internal/backend/test/config.go b/internal/backend/test/config.go index 77172dc86..496ba2761 100644 --- a/internal/backend/test/config.go +++ b/internal/backend/test/config.go @@ -11,7 +11,7 @@ type ConfigTestData[C comparable] struct { Cfg C } -func ParseConfigTester[C comparable](t *testing.T, parser func(s string) (C, error), tests []ConfigTestData[C]) { +func ParseConfigTester[C comparable](t *testing.T, parser func(s string) (*C, error), tests []ConfigTestData[C]) { for i, test := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { cfg, err := parser(test.S) @@ -19,9 +19,9 @@ func ParseConfigTester[C comparable](t *testing.T, parser func(s string) (C, err t.Fatalf("%s failed: %v", test.S, err) } - if !reflect.DeepEqual(cfg, test.Cfg) { + if !reflect.DeepEqual(*cfg, test.Cfg) { t.Fatalf("input: %s\n wrong config, want:\n %#v\ngot:\n %#v", - test.S, test.Cfg, cfg) + test.S, test.Cfg, *cfg) } }) } diff --git a/internal/backend/test/suite.go b/internal/backend/test/suite.go index fcb1b00b4..75ae0630b 100644 --- a/internal/backend/test/suite.go +++ b/internal/backend/test/suite.go @@ -13,10 +13,10 @@ import ( // Suite implements a test suite for restic backends. type Suite[C any] struct { // Config should be used to configure the backend. - Config C + Config *C // NewConfig returns a config for a new temporary backend that will be used in tests. - NewConfig func() (C, error) + NewConfig func() (*C, error) // CreateFn is a function that creates a temporary repository for the tests. Create func(cfg C) (restic.Backend, error) @@ -61,7 +61,7 @@ func (s *Suite[C]) RunTests(t *testing.T) { } if s.Cleanup != nil { - if err = s.Cleanup(s.Config); err != nil { + if err = s.Cleanup(*s.Config); err != nil { t.Fatal(err) } } @@ -158,13 +158,13 @@ func (s *Suite[C]) RunBenchmarks(b *testing.B) { return } - if err = s.Cleanup(s.Config); err != nil { + if err = s.Cleanup(*s.Config); err != nil { b.Fatal(err) } } func (s *Suite[C]) create(t testing.TB) restic.Backend { - be, err := s.Create(s.Config) + be, err := s.Create(*s.Config) if err != nil { t.Fatal(err) } @@ -172,7 +172,7 @@ func (s *Suite[C]) create(t testing.TB) restic.Backend { } func (s *Suite[C]) open(t testing.TB) restic.Backend { - be, err := s.Open(s.Config) + be, err := s.Open(*s.Config) if err != nil { t.Fatal(err) } diff --git a/internal/backend/test/tests.go b/internal/backend/test/tests.go index b9dcc9ba5..9851bf184 100644 --- a/internal/backend/test/tests.go +++ b/internal/backend/test/tests.go @@ -57,7 +57,7 @@ func (s *Suite[C]) TestCreateWithConfig(t *testing.T) { store(t, b, restic.ConfigFile, []byte("test config")) // now create the backend again, this must fail - _, err = s.Create(s.Config) + _, err = s.Create(*s.Config) if err == nil { t.Fatalf("expected error not found for creating a backend with an existing config file") }