diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c878e5085..9c1b5384d80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * [ENHANCEMENT] Upgraded Docker base images to `alpine:3.14`. #4514 * [ENHANCEMENT] Updated Prometheus to latest. Includes changes from prometheus#9239, adding 15 new functions. Multiple TSDB bugfixes prometheus#9438 & prometheus#9381. #4524 * [ENHANCEMENT] Query Frontend: Add setting `-frontend.forward-headers-list` in frontend to configure the set of headers from the requests to be forwarded to downstream requests. #4486 +* [ENHANCEMENT] Blocks storage: Add `-blocks-storage.azure.http.*`, `-alertmanager-storage.azure.http.*`, and `-ruler-storage.azure.http.*` to configure the Azure storage client. #4581 * [BUGFIX] AlertManager: remove stale template files. #4495 ## 1.11.0 2021-11-25 diff --git a/docs/blocks-storage/querier.md b/docs/blocks-storage/querier.md index 9bfb2a44807..25442c6b992 100644 --- a/docs/blocks-storage/querier.md +++ b/docs/blocks-storage/querier.md @@ -278,8 +278,8 @@ blocks_storage: # CLI flag: -blocks-storage.s3.http.response-header-timeout [response_header_timeout: | default = 2m] - # If the client connects to S3 via HTTPS and this option is enabled, the - # client will accept any certificate and hostname. + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. # CLI flag: -blocks-storage.s3.http.insecure-skip-verify [insecure_skip_verify: | default = false] @@ -340,6 +340,44 @@ blocks_storage: # CLI flag: -blocks-storage.azure.max-retries [max_retries: | default = 20] + http: + # The time an idle connection will remain idle before closing. + # CLI flag: -blocks-storage.azure.http.idle-conn-timeout + [idle_conn_timeout: | default = 1m30s] + + # The amount of time the client will wait for a servers response headers. + # CLI flag: -blocks-storage.azure.http.response-header-timeout + [response_header_timeout: | default = 2m] + + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. + # CLI flag: -blocks-storage.azure.http.insecure-skip-verify + [insecure_skip_verify: | default = false] + + # Maximum time to wait for a TLS handshake. 0 means no limit. + # CLI flag: -blocks-storage.azure.tls-handshake-timeout + [tls_handshake_timeout: | default = 10s] + + # The time to wait for a server's first response headers after fully + # writing the request headers if the request has an Expect header. 0 to + # send the request body immediately. + # CLI flag: -blocks-storage.azure.expect-continue-timeout + [expect_continue_timeout: | default = 1s] + + # Maximum number of idle (keep-alive) connections across all hosts. 0 + # means no limit. + # CLI flag: -blocks-storage.azure.max-idle-connections + [max_idle_connections: | default = 100] + + # Maximum number of idle (keep-alive) connections to keep per-host. If 0, + # a built-in default value is used. + # CLI flag: -blocks-storage.azure.max-idle-connections-per-host + [max_idle_connections_per_host: | default = 100] + + # Maximum number of connections per host. 0 means no limit. + # CLI flag: -blocks-storage.azure.max-connections-per-host + [max_connections_per_host: | default = 0] + swift: # OpenStack Swift authentication API version. 0 to autodetect. # CLI flag: -blocks-storage.swift.auth-version diff --git a/docs/blocks-storage/store-gateway.md b/docs/blocks-storage/store-gateway.md index d82fdcfd99c..6ab87e7eda2 100644 --- a/docs/blocks-storage/store-gateway.md +++ b/docs/blocks-storage/store-gateway.md @@ -342,8 +342,8 @@ blocks_storage: # CLI flag: -blocks-storage.s3.http.response-header-timeout [response_header_timeout: | default = 2m] - # If the client connects to S3 via HTTPS and this option is enabled, the - # client will accept any certificate and hostname. + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. # CLI flag: -blocks-storage.s3.http.insecure-skip-verify [insecure_skip_verify: | default = false] @@ -404,6 +404,44 @@ blocks_storage: # CLI flag: -blocks-storage.azure.max-retries [max_retries: | default = 20] + http: + # The time an idle connection will remain idle before closing. + # CLI flag: -blocks-storage.azure.http.idle-conn-timeout + [idle_conn_timeout: | default = 1m30s] + + # The amount of time the client will wait for a servers response headers. + # CLI flag: -blocks-storage.azure.http.response-header-timeout + [response_header_timeout: | default = 2m] + + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. + # CLI flag: -blocks-storage.azure.http.insecure-skip-verify + [insecure_skip_verify: | default = false] + + # Maximum time to wait for a TLS handshake. 0 means no limit. + # CLI flag: -blocks-storage.azure.tls-handshake-timeout + [tls_handshake_timeout: | default = 10s] + + # The time to wait for a server's first response headers after fully + # writing the request headers if the request has an Expect header. 0 to + # send the request body immediately. + # CLI flag: -blocks-storage.azure.expect-continue-timeout + [expect_continue_timeout: | default = 1s] + + # Maximum number of idle (keep-alive) connections across all hosts. 0 + # means no limit. + # CLI flag: -blocks-storage.azure.max-idle-connections + [max_idle_connections: | default = 100] + + # Maximum number of idle (keep-alive) connections to keep per-host. If 0, + # a built-in default value is used. + # CLI flag: -blocks-storage.azure.max-idle-connections-per-host + [max_idle_connections_per_host: | default = 100] + + # Maximum number of connections per host. 0 means no limit. + # CLI flag: -blocks-storage.azure.max-connections-per-host + [max_connections_per_host: | default = 0] + swift: # OpenStack Swift authentication API version. 0 to autodetect. # CLI flag: -blocks-storage.swift.auth-version diff --git a/docs/configuration/config-file-reference.md b/docs/configuration/config-file-reference.md index 3c4ec3f0b91..047fcaba2c9 100644 --- a/docs/configuration/config-file-reference.md +++ b/docs/configuration/config-file-reference.md @@ -1714,8 +1714,8 @@ s3: # CLI flag: -ruler-storage.s3.http.response-header-timeout [response_header_timeout: | default = 2m] - # If the client connects to S3 via HTTPS and this option is enabled, the - # client will accept any certificate and hostname. + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. # CLI flag: -ruler-storage.s3.http.insecure-skip-verify [insecure_skip_verify: | default = false] @@ -1776,6 +1776,44 @@ azure: # CLI flag: -ruler-storage.azure.max-retries [max_retries: | default = 20] + http: + # The time an idle connection will remain idle before closing. + # CLI flag: -ruler-storage.azure.http.idle-conn-timeout + [idle_conn_timeout: | default = 1m30s] + + # The amount of time the client will wait for a servers response headers. + # CLI flag: -ruler-storage.azure.http.response-header-timeout + [response_header_timeout: | default = 2m] + + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. + # CLI flag: -ruler-storage.azure.http.insecure-skip-verify + [insecure_skip_verify: | default = false] + + # Maximum time to wait for a TLS handshake. 0 means no limit. + # CLI flag: -ruler-storage.azure.tls-handshake-timeout + [tls_handshake_timeout: | default = 10s] + + # The time to wait for a server's first response headers after fully writing + # the request headers if the request has an Expect header. 0 to send the + # request body immediately. + # CLI flag: -ruler-storage.azure.expect-continue-timeout + [expect_continue_timeout: | default = 1s] + + # Maximum number of idle (keep-alive) connections across all hosts. 0 means + # no limit. + # CLI flag: -ruler-storage.azure.max-idle-connections + [max_idle_connections: | default = 100] + + # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a + # built-in default value is used. + # CLI flag: -ruler-storage.azure.max-idle-connections-per-host + [max_idle_connections_per_host: | default = 100] + + # Maximum number of connections per host. 0 means no limit. + # CLI flag: -ruler-storage.azure.max-connections-per-host + [max_connections_per_host: | default = 0] + swift: # OpenStack Swift authentication API version. 0 to autodetect. # CLI flag: -ruler-storage.swift.auth-version @@ -2258,8 +2296,8 @@ s3: # CLI flag: -alertmanager-storage.s3.http.response-header-timeout [response_header_timeout: | default = 2m] - # If the client connects to S3 via HTTPS and this option is enabled, the - # client will accept any certificate and hostname. + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. # CLI flag: -alertmanager-storage.s3.http.insecure-skip-verify [insecure_skip_verify: | default = false] @@ -2320,6 +2358,44 @@ azure: # CLI flag: -alertmanager-storage.azure.max-retries [max_retries: | default = 20] + http: + # The time an idle connection will remain idle before closing. + # CLI flag: -alertmanager-storage.azure.http.idle-conn-timeout + [idle_conn_timeout: | default = 1m30s] + + # The amount of time the client will wait for a servers response headers. + # CLI flag: -alertmanager-storage.azure.http.response-header-timeout + [response_header_timeout: | default = 2m] + + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. + # CLI flag: -alertmanager-storage.azure.http.insecure-skip-verify + [insecure_skip_verify: | default = false] + + # Maximum time to wait for a TLS handshake. 0 means no limit. + # CLI flag: -alertmanager-storage.azure.tls-handshake-timeout + [tls_handshake_timeout: | default = 10s] + + # The time to wait for a server's first response headers after fully writing + # the request headers if the request has an Expect header. 0 to send the + # request body immediately. + # CLI flag: -alertmanager-storage.azure.expect-continue-timeout + [expect_continue_timeout: | default = 1s] + + # Maximum number of idle (keep-alive) connections across all hosts. 0 means + # no limit. + # CLI flag: -alertmanager-storage.azure.max-idle-connections + [max_idle_connections: | default = 100] + + # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a + # built-in default value is used. + # CLI flag: -alertmanager-storage.azure.max-idle-connections-per-host + [max_idle_connections_per_host: | default = 100] + + # Maximum number of connections per host. 0 means no limit. + # CLI flag: -alertmanager-storage.azure.max-connections-per-host + [max_connections_per_host: | default = 0] + swift: # OpenStack Swift authentication API version. 0 to autodetect. # CLI flag: -alertmanager-storage.swift.auth-version @@ -4565,8 +4641,8 @@ s3: # CLI flag: -blocks-storage.s3.http.response-header-timeout [response_header_timeout: | default = 2m] - # If the client connects to S3 via HTTPS and this option is enabled, the - # client will accept any certificate and hostname. + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. # CLI flag: -blocks-storage.s3.http.insecure-skip-verify [insecure_skip_verify: | default = false] @@ -4627,6 +4703,44 @@ azure: # CLI flag: -blocks-storage.azure.max-retries [max_retries: | default = 20] + http: + # The time an idle connection will remain idle before closing. + # CLI flag: -blocks-storage.azure.http.idle-conn-timeout + [idle_conn_timeout: | default = 1m30s] + + # The amount of time the client will wait for a servers response headers. + # CLI flag: -blocks-storage.azure.http.response-header-timeout + [response_header_timeout: | default = 2m] + + # If the client connects via HTTPS and this option is enabled, the client + # will accept any certificate and hostname. + # CLI flag: -blocks-storage.azure.http.insecure-skip-verify + [insecure_skip_verify: | default = false] + + # Maximum time to wait for a TLS handshake. 0 means no limit. + # CLI flag: -blocks-storage.azure.tls-handshake-timeout + [tls_handshake_timeout: | default = 10s] + + # The time to wait for a server's first response headers after fully writing + # the request headers if the request has an Expect header. 0 to send the + # request body immediately. + # CLI flag: -blocks-storage.azure.expect-continue-timeout + [expect_continue_timeout: | default = 1s] + + # Maximum number of idle (keep-alive) connections across all hosts. 0 means + # no limit. + # CLI flag: -blocks-storage.azure.max-idle-connections + [max_idle_connections: | default = 100] + + # Maximum number of idle (keep-alive) connections to keep per-host. If 0, a + # built-in default value is used. + # CLI flag: -blocks-storage.azure.max-idle-connections-per-host + [max_idle_connections_per_host: | default = 100] + + # Maximum number of connections per host. 0 means no limit. + # CLI flag: -blocks-storage.azure.max-connections-per-host + [max_connections_per_host: | default = 0] + swift: # OpenStack Swift authentication API version. 0 to autodetect. # CLI flag: -blocks-storage.swift.auth-version diff --git a/docs/contributing/how-to-run-website-locally.md b/docs/contributing/how-to-run-website-locally.md index abea6fdc21a..5de852d8d70 100644 --- a/docs/contributing/how-to-run-website-locally.md +++ b/docs/contributing/how-to-run-website-locally.md @@ -18,7 +18,11 @@ The following initial setup is required only once: ``` cd website && npm install && cd - ``` -4. Run `make BUILD_IN_CONTAINER=false web-build` +4. Install [embedmd](https://github.com/campoy/embedmd) `v1.0.0`: + ``` + go install github.com/campoy/embedmd@v1.0.0 + ``` +5. Run `make BUILD_IN_CONTAINER=false web-build` ## Run it diff --git a/pkg/storage/bucket/azure/bucket_client.go b/pkg/storage/bucket/azure/bucket_client.go index 522330b4f9d..3e9b9bed993 100644 --- a/pkg/storage/bucket/azure/bucket_client.go +++ b/pkg/storage/bucket/azure/bucket_client.go @@ -2,6 +2,7 @@ package azure import ( "github.com/go-kit/log" + "github.com/prometheus/common/model" "github.com/thanos-io/thanos/pkg/objstore" "github.com/thanos-io/thanos/pkg/objstore/azure" yaml "gopkg.in/yaml.v2" @@ -14,6 +15,16 @@ func NewBucketClient(cfg Config, name string, logger log.Logger) (objstore.Bucke ContainerName: cfg.ContainerName, Endpoint: cfg.Endpoint, MaxRetries: cfg.MaxRetries, + HTTPConfig: azure.HTTPConfig{ + IdleConnTimeout: model.Duration(cfg.IdleConnTimeout), + ResponseHeaderTimeout: model.Duration(cfg.ResponseHeaderTimeout), + InsecureSkipVerify: cfg.InsecureSkipVerify, + TLSHandshakeTimeout: model.Duration(cfg.TLSHandshakeTimeout), + ExpectContinueTimeout: model.Duration(cfg.ExpectContinueTimeout), + MaxIdleConns: cfg.MaxIdleConns, + MaxIdleConnsPerHost: cfg.MaxIdleConnsPerHost, + MaxConnsPerHost: cfg.MaxConnsPerHost, + }, } // Thanos currently doesn't support passing the config as is, but expects a YAML, diff --git a/pkg/storage/bucket/azure/config.go b/pkg/storage/bucket/azure/config.go index df17f76bff9..eb41b5f4f08 100644 --- a/pkg/storage/bucket/azure/config.go +++ b/pkg/storage/bucket/azure/config.go @@ -4,6 +4,8 @@ import ( "flag" "github.com/grafana/dskit/flagext" + + "github.com/cortexproject/cortex/pkg/storage/bucket/http" ) // Config holds the config options for an Azure backend @@ -13,6 +15,8 @@ type Config struct { ContainerName string `yaml:"container_name"` Endpoint string `yaml:"endpoint_suffix"` MaxRetries int `yaml:"max_retries"` + + http.Config `yaml:"http"` } // RegisterFlags registers the flags for Azure storage @@ -27,4 +31,5 @@ func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { f.StringVar(&cfg.ContainerName, prefix+"azure.container-name", "", "Azure storage container name") f.StringVar(&cfg.Endpoint, prefix+"azure.endpoint-suffix", "", "Azure storage endpoint suffix without schema. The account name will be prefixed to this value to create the FQDN") f.IntVar(&cfg.MaxRetries, prefix+"azure.max-retries", 20, "Number of retries for recoverable errors") + cfg.Config.RegisterFlagsWithPrefix(prefix+"azure.", f) } diff --git a/pkg/storage/bucket/azure/config_test.go b/pkg/storage/bucket/azure/config_test.go new file mode 100644 index 00000000000..3c6bb147e23 --- /dev/null +++ b/pkg/storage/bucket/azure/config_test.go @@ -0,0 +1,97 @@ +package azure + +import ( + "testing" + "time" + + "github.com/grafana/dskit/flagext" + "github.com/stretchr/testify/require" + yaml "gopkg.in/yaml.v2" + + "github.com/cortexproject/cortex/pkg/storage/bucket/http" +) + +// defaultConfig should match the default flag values defined in RegisterFlagsWithPrefix. +var defaultConfig = Config{ + MaxRetries: 20, + Config: http.Config{ + IdleConnTimeout: 90 * time.Second, + ResponseHeaderTimeout: 2 * time.Minute, + InsecureSkipVerify: false, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 100, + MaxConnsPerHost: 0, + }, +} + +func TestConfig(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + config string + expectedConfig Config + expectedErr error + }{ + "default config": { + config: "", + expectedConfig: defaultConfig, + expectedErr: nil, + }, + "custom config": { + config: ` +account_name: test-account-name +account_key: test-account-key +container_name: test-container-name +endpoint_suffix: test-endpoint-suffix +max_retries: 1 +http: + idle_conn_timeout: 2s + response_header_timeout: 3s + insecure_skip_verify: true + tls_handshake_timeout: 4s + expect_continue_timeout: 5s + max_idle_connections: 6 + max_idle_connections_per_host: 7 + max_connections_per_host: 8 +`, + expectedConfig: Config{ + StorageAccountName: "test-account-name", + StorageAccountKey: flagext.Secret{Value: "test-account-key"}, + ContainerName: "test-container-name", + Endpoint: "test-endpoint-suffix", + MaxRetries: 1, + Config: http.Config{ + IdleConnTimeout: 2 * time.Second, + ResponseHeaderTimeout: 3 * time.Second, + InsecureSkipVerify: true, + TLSHandshakeTimeout: 4 * time.Second, + ExpectContinueTimeout: 5 * time.Second, + MaxIdleConns: 6, + MaxIdleConnsPerHost: 7, + MaxConnsPerHost: 8, + }, + }, + expectedErr: nil, + }, + "invalid type": { + config: `max_retries: foo`, + expectedConfig: defaultConfig, + expectedErr: &yaml.TypeError{Errors: []string{"line 1: cannot unmarshal !!str `foo` into int"}}, + }, + } + + for testName, testData := range tests { + testData := testData + + t.Run(testName, func(t *testing.T) { + cfg := Config{} + flagext.DefaultValues(&cfg) + + err := yaml.Unmarshal([]byte(testData.config), &cfg) + require.Equal(t, testData.expectedErr, err) + require.Equal(t, testData.expectedConfig, cfg) + }) + } +} diff --git a/pkg/storage/bucket/client.go b/pkg/storage/bucket/client.go index d6515ca93b0..245c3edef43 100644 --- a/pkg/storage/bucket/client.go +++ b/pkg/storage/bucket/client.go @@ -78,7 +78,7 @@ func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { cfg.Swift.RegisterFlagsWithPrefix(prefix, f) cfg.Filesystem.RegisterFlagsWithPrefix(prefix, f) - f.StringVar(&cfg.Backend, prefix+"backend", "s3", fmt.Sprintf("Backend storage to use. Supported backends are: %s.", strings.Join(cfg.supportedBackends(), ", "))) + f.StringVar(&cfg.Backend, prefix+"backend", S3, fmt.Sprintf("Backend storage to use. Supported backends are: %s.", strings.Join(cfg.supportedBackends(), ", "))) } func (cfg *Config) Validate() error { diff --git a/pkg/storage/bucket/http/config.go b/pkg/storage/bucket/http/config.go new file mode 100644 index 00000000000..1c83e1f311b --- /dev/null +++ b/pkg/storage/bucket/http/config.go @@ -0,0 +1,35 @@ +package http + +import ( + "flag" + "time" +) + +// Config stores the http.Client configuration for the storage clients. +type Config struct { + IdleConnTimeout time.Duration `yaml:"idle_conn_timeout"` + ResponseHeaderTimeout time.Duration `yaml:"response_header_timeout"` + InsecureSkipVerify bool `yaml:"insecure_skip_verify"` + TLSHandshakeTimeout time.Duration `yaml:"tls_handshake_timeout"` + ExpectContinueTimeout time.Duration `yaml:"expect_continue_timeout"` + MaxIdleConns int `yaml:"max_idle_connections"` + MaxIdleConnsPerHost int `yaml:"max_idle_connections_per_host"` + MaxConnsPerHost int `yaml:"max_connections_per_host"` +} + +// RegisterFlags registers the flags for the storage HTTP client. +func (cfg *Config) RegisterFlags(f *flag.FlagSet) { + cfg.RegisterFlagsWithPrefix("", f) +} + +// RegisterFlagsWithPrefix registers the flags for the storage HTTP client with the provided prefix. +func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { + f.DurationVar(&cfg.IdleConnTimeout, prefix+"http.idle-conn-timeout", 90*time.Second, "The time an idle connection will remain idle before closing.") + f.DurationVar(&cfg.ResponseHeaderTimeout, prefix+"http.response-header-timeout", 2*time.Minute, "The amount of time the client will wait for a servers response headers.") + f.BoolVar(&cfg.InsecureSkipVerify, prefix+"http.insecure-skip-verify", false, "If the client connects via HTTPS and this option is enabled, the client will accept any certificate and hostname.") + f.DurationVar(&cfg.TLSHandshakeTimeout, prefix+"tls-handshake-timeout", 10*time.Second, "Maximum time to wait for a TLS handshake. 0 means no limit.") + f.DurationVar(&cfg.ExpectContinueTimeout, prefix+"expect-continue-timeout", 1*time.Second, "The time to wait for a server's first response headers after fully writing the request headers if the request has an Expect header. 0 to send the request body immediately.") + f.IntVar(&cfg.MaxIdleConns, prefix+"max-idle-connections", 100, "Maximum number of idle (keep-alive) connections across all hosts. 0 means no limit.") + f.IntVar(&cfg.MaxIdleConnsPerHost, prefix+"max-idle-connections-per-host", 100, "Maximum number of idle (keep-alive) connections to keep per-host. If 0, a built-in default value is used.") + f.IntVar(&cfg.MaxConnsPerHost, prefix+"max-connections-per-host", 0, "Maximum number of connections per host. 0 means no limit.") +} diff --git a/pkg/storage/bucket/http/config_test.go b/pkg/storage/bucket/http/config_test.go new file mode 100644 index 00000000000..d7da35e581a --- /dev/null +++ b/pkg/storage/bucket/http/config_test.go @@ -0,0 +1,79 @@ +package http + +import ( + "testing" + "time" + + "github.com/grafana/dskit/flagext" + "github.com/stretchr/testify/require" + yaml "gopkg.in/yaml.v2" +) + +// defaultConfig should match the default flag values defined in RegisterFlagsWithPrefix. +var defaultConfig = Config{ + IdleConnTimeout: 90 * time.Second, + ResponseHeaderTimeout: 2 * time.Minute, + InsecureSkipVerify: false, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 100, + MaxConnsPerHost: 0, +} + +func TestConfig(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + config string + expectedConfig Config + expectedErr error + }{ + "default config": { + config: "", + expectedConfig: defaultConfig, + expectedErr: nil, + }, + "custom config": { + config: ` +idle_conn_timeout: 2s +response_header_timeout: 3s +insecure_skip_verify: true +tls_handshake_timeout: 4s +expect_continue_timeout: 5s +max_idle_connections: 6 +max_idle_connections_per_host: 7 +max_connections_per_host: 8 +`, + expectedConfig: Config{ + IdleConnTimeout: 2 * time.Second, + ResponseHeaderTimeout: 3 * time.Second, + InsecureSkipVerify: true, + TLSHandshakeTimeout: 4 * time.Second, + ExpectContinueTimeout: 5 * time.Second, + MaxIdleConns: 6, + MaxIdleConnsPerHost: 7, + MaxConnsPerHost: 8, + }, + expectedErr: nil, + }, + "invalid type": { + config: `max_idle_connections: foo`, + expectedConfig: defaultConfig, + expectedErr: &yaml.TypeError{Errors: []string{"line 1: cannot unmarshal !!str `foo` into int"}}, + }, + } + + for testName, testData := range tests { + testData := testData + + t.Run(testName, func(t *testing.T) { + cfg := Config{} + flagext.DefaultValues(&cfg) + + err := yaml.Unmarshal([]byte(testData.config), &cfg) + require.Equal(t, testData.expectedErr, err) + require.Equal(t, testData.expectedConfig, cfg) + }) + } +} diff --git a/pkg/storage/bucket/s3/config.go b/pkg/storage/bucket/s3/config.go index 156edba2ef2..4bbd7051f9f 100644 --- a/pkg/storage/bucket/s3/config.go +++ b/pkg/storage/bucket/s3/config.go @@ -6,13 +6,13 @@ import ( "fmt" "net/http" "strings" - "time" "github.com/grafana/dskit/flagext" "github.com/minio/minio-go/v7/pkg/encrypt" "github.com/pkg/errors" "github.com/thanos-io/thanos/pkg/objstore/s3" + bucket_http "github.com/cortexproject/cortex/pkg/storage/bucket/http" "github.com/cortexproject/cortex/pkg/util" ) @@ -39,14 +39,7 @@ var ( // HTTPConfig stores the http.Transport configuration for the s3 minio client. type HTTPConfig struct { - IdleConnTimeout time.Duration `yaml:"idle_conn_timeout"` - ResponseHeaderTimeout time.Duration `yaml:"response_header_timeout"` - InsecureSkipVerify bool `yaml:"insecure_skip_verify"` - TLSHandshakeTimeout time.Duration `yaml:"tls_handshake_timeout"` - ExpectContinueTimeout time.Duration `yaml:"expect_continue_timeout"` - MaxIdleConns int `yaml:"max_idle_connections"` - MaxIdleConnsPerHost int `yaml:"max_idle_connections_per_host"` - MaxConnsPerHost int `yaml:"max_connections_per_host"` + bucket_http.Config `yaml:",inline"` // Allow upstream callers to inject a round tripper Transport http.RoundTripper `yaml:"-"` @@ -54,14 +47,7 @@ type HTTPConfig struct { // RegisterFlagsWithPrefix registers the flags for s3 storage with the provided prefix func (cfg *HTTPConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { - f.DurationVar(&cfg.IdleConnTimeout, prefix+"s3.http.idle-conn-timeout", 90*time.Second, "The time an idle connection will remain idle before closing.") - f.DurationVar(&cfg.ResponseHeaderTimeout, prefix+"s3.http.response-header-timeout", 2*time.Minute, "The amount of time the client will wait for a servers response headers.") - f.BoolVar(&cfg.InsecureSkipVerify, prefix+"s3.http.insecure-skip-verify", false, "If the client connects to S3 via HTTPS and this option is enabled, the client will accept any certificate and hostname.") - f.DurationVar(&cfg.TLSHandshakeTimeout, prefix+"s3.tls-handshake-timeout", 10*time.Second, "Maximum time to wait for a TLS handshake. 0 means no limit.") - f.DurationVar(&cfg.ExpectContinueTimeout, prefix+"s3.expect-continue-timeout", 1*time.Second, "The time to wait for a server's first response headers after fully writing the request headers if the request has an Expect header. 0 to send the request body immediately.") - f.IntVar(&cfg.MaxIdleConns, prefix+"s3.max-idle-connections", 100, "Maximum number of idle (keep-alive) connections across all hosts. 0 means no limit.") - f.IntVar(&cfg.MaxIdleConnsPerHost, prefix+"s3.max-idle-connections-per-host", 100, "Maximum number of idle (keep-alive) connections to keep per-host. If 0, a built-in default value is used.") - f.IntVar(&cfg.MaxConnsPerHost, prefix+"s3.max-connections-per-host", 0, "Maximum number of connections per host. 0 means no limit.") + cfg.Config.RegisterFlagsWithPrefix(prefix+"s3.", f) } // Config holds the config options for an S3 backend diff --git a/pkg/storage/bucket/s3/config_test.go b/pkg/storage/bucket/s3/config_test.go index 253a2ce5991..4e02ee5006f 100644 --- a/pkg/storage/bucket/s3/config_test.go +++ b/pkg/storage/bucket/s3/config_test.go @@ -4,12 +4,118 @@ import ( "encoding/base64" "net/http" "testing" + "time" "github.com/grafana/dskit/flagext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + + bucket_http "github.com/cortexproject/cortex/pkg/storage/bucket/http" ) +// defaultConfig should match the default flag values defined in RegisterFlagsWithPrefix. +var defaultConfig = Config{ + SignatureVersion: SignatureVersionV4, + HTTP: HTTPConfig{ + Config: bucket_http.Config{ + IdleConnTimeout: 90 * time.Second, + ResponseHeaderTimeout: 2 * time.Minute, + InsecureSkipVerify: false, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + MaxIdleConns: 100, + MaxIdleConnsPerHost: 100, + MaxConnsPerHost: 0, + }, + }, +} + +func TestConfig(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + config string + expectedConfig Config + expectedErr error + }{ + "default config": { + config: "", + expectedConfig: defaultConfig, + expectedErr: nil, + }, + "custom config": { + config: ` +endpoint: test-endpoint +region: test-region +bucket_name: test-bucket-name +secret_access_key: test-secret-access-key +access_key_id: test-access-key-id +insecure: true +signature_version: test-signature-version +sse: + type: test-type + kms_key_id: test-kms-key-id + kms_encryption_context: test-kms-encryption-context +http: + idle_conn_timeout: 2s + response_header_timeout: 3s + insecure_skip_verify: true + tls_handshake_timeout: 4s + expect_continue_timeout: 5s + max_idle_connections: 6 + max_idle_connections_per_host: 7 + max_connections_per_host: 8 +`, + expectedConfig: Config{ + Endpoint: "test-endpoint", + Region: "test-region", + BucketName: "test-bucket-name", + SecretAccessKey: flagext.Secret{Value: "test-secret-access-key"}, + AccessKeyID: "test-access-key-id", + Insecure: true, + SignatureVersion: "test-signature-version", + SSE: SSEConfig{ + Type: "test-type", + KMSKeyID: "test-kms-key-id", + KMSEncryptionContext: "test-kms-encryption-context", + }, + HTTP: HTTPConfig{ + Config: bucket_http.Config{ + IdleConnTimeout: 2 * time.Second, + ResponseHeaderTimeout: 3 * time.Second, + InsecureSkipVerify: true, + TLSHandshakeTimeout: 4 * time.Second, + ExpectContinueTimeout: 5 * time.Second, + MaxIdleConns: 6, + MaxIdleConnsPerHost: 7, + MaxConnsPerHost: 8, + }, + }, + }, + expectedErr: nil, + }, + "invalid type": { + config: `insecure: foo`, + expectedConfig: defaultConfig, + expectedErr: &yaml.TypeError{Errors: []string{"line 1: cannot unmarshal !!str `foo` into bool"}}, + }, + } + + for testName, testData := range tests { + testData := testData + + t.Run(testName, func(t *testing.T) { + cfg := Config{} + flagext.DefaultValues(&cfg) + + err := yaml.Unmarshal([]byte(testData.config), &cfg) + require.Equal(t, testData.expectedErr, err) + require.Equal(t, testData.expectedConfig, cfg) + }) + } +} + func TestSSEConfig_Validate(t *testing.T) { tests := map[string]struct { setup func() *SSEConfig