diff --git a/internal/gitaly/config/config.go b/internal/gitaly/config/config.go index 4f92c006f6aaeee7d8b80e19b8a81b98e5e5a742..3870fa6cf59483c0420d62df680d0f4273783f8e 100644 --- a/internal/gitaly/config/config.go +++ b/internal/gitaly/config/config.go @@ -918,6 +918,26 @@ func (cfg *Cfg) Sanitize() error { cfg.Logging.Config.Level = "info" } + // Go Cloud Development Kit (CDK) v0.39.0 introduced a default to AWS SDK v2, + // which includes a new token bucket rate limiting mechanism. This can cause + // unexpected behavior in some scenarios. To mitigate this, we use the 'awssdk=v1' + // query parameter to revert to v1 behavior. This workaround is temporary; + // Go CDK v0.40.0 will default to no rate limiting unless explicitly defined. + if cfg.Backup.GoCloudURL != "" { + parsedURL, err := url.Parse(cfg.Backup.GoCloudURL) + if err != nil { + return fmt.Errorf("parse GoCloudURL: %w", err) + } + if parsedURL.Scheme == "s3" { + query := parsedURL.Query() + if query.Get("awssdk") == "" { + query.Set("awssdk", "v1") + parsedURL.RawQuery = query.Encode() + cfg.Backup.GoCloudURL = parsedURL.String() + } + } + } + return nil } diff --git a/internal/gitaly/config/config_test.go b/internal/gitaly/config/config_test.go index dcc94004ad17a2c3db9a0615961e32f1d3e7ffdb..3c3b048a43702c65b226a4703d640c85f6631bbe 100644 --- a/internal/gitaly/config/config_test.go +++ b/internal/gitaly/config/config_test.go @@ -2967,3 +2967,71 @@ initial_members = {1 = "localhost:4001", 2 = "localhost:4002", 3 = "localhost:40 require.NoError(t, expectedCfg.Sanitize()) require.Equal(t, expectedCfg.Raft, cfg.Raft) } + +func TestGoCloudURL(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + cfg Cfg + expectedURL string + expectedErr error + }{ + { + name: "invalid url provided", + cfg: Cfg{ + Backup: BackupConfig{ + GoCloudURL: "s3://my bucket", + }, + }, + expectedErr: fmt.Errorf(""), + }, + { + name: "url is not s3", + cfg: Cfg{ + Backup: BackupConfig{ + GoCloudURL: "gs://my-bucket", + }, + }, + expectedURL: "gs://my-bucket", + }, + { + name: "no query parameter provided", + cfg: Cfg{ + Backup: BackupConfig{ + GoCloudURL: "s3://my-bucket", + }, + }, + expectedURL: "s3://my-bucket?awssdk=v1", + }, + { + name: "query parameter other than awssdk provided", + cfg: Cfg{ + Backup: BackupConfig{ + GoCloudURL: "s3://my-bucket?foo=bar", + }, + }, + expectedURL: "s3://my-bucket?awssdk=v1&foo=bar", + }, + { + name: "awssdk query parameter provided", + cfg: Cfg{ + Backup: BackupConfig{ + GoCloudURL: "s3://my-bucket?awssdk=v2", + }, + }, + expectedURL: "s3://my-bucket?awssdk=v2", + }, + } { + t.Run(tc.name, func(t *testing.T) { + err := tc.cfg.Sanitize() + + if tc.expectedErr == nil { + require.NoError(t, err) + require.Equal(t, tc.expectedURL, tc.cfg.Backup.GoCloudURL) + } else { + require.Error(t, err, "parse GoCloudURL: parse \"s3://my bucket\": invalid character") + } + }) + } +}