diff --git a/lib/promauth/config.go b/lib/promauth/config.go index 3da9a66971..4c66ec3595 100644 --- a/lib/promauth/config.go +++ b/lib/promauth/config.go @@ -123,6 +123,9 @@ type HTTPClientConfig struct { // Headers contains optional HTTP headers, which must be sent in the request to the server Headers []string `yaml:"headers,omitempty"` + + // FollowRedirects specifies whether the client should follow HTTP 3xx redirects. + FollowRedirects *bool `yaml:"follow_redirects,omitempty"` } // ProxyClientConfig represents proxy client config. diff --git a/lib/promscrape/config.go b/lib/promscrape/config.go index e5c07eff11..cc1b9529af 100644 --- a/lib/promscrape/config.go +++ b/lib/promscrape/config.go @@ -245,7 +245,6 @@ type ScrapeConfig struct { MetricsPath string `yaml:"metrics_path,omitempty"` HonorLabels bool `yaml:"honor_labels,omitempty"` HonorTimestamps *bool `yaml:"honor_timestamps,omitempty"` - FollowRedirects *bool `yaml:"follow_redirects,omitempty"` Scheme string `yaml:"scheme,omitempty"` Params map[string][]string `yaml:"params,omitempty"` HTTPClientConfig promauth.HTTPClientConfig `yaml:",inline"` @@ -990,8 +989,8 @@ func getScrapeWorkConfig(sc *ScrapeConfig, baseDir string, globalCfg *GlobalConf honorTimestamps = *sc.HonorTimestamps } denyRedirects := false - if sc.FollowRedirects != nil { - denyRedirects = !*sc.FollowRedirects + if sc.HTTPClientConfig.FollowRedirects != nil { + denyRedirects = !*sc.HTTPClientConfig.FollowRedirects } metricsPath := sc.MetricsPath if metricsPath == "" { diff --git a/lib/promscrape/discovery/azure/api.go b/lib/promscrape/discovery/azure/api.go index 2ff0841936..bd580cec06 100644 --- a/lib/promscrape/discovery/azure/api.go +++ b/lib/promscrape/discovery/azure/api.go @@ -110,7 +110,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, err } - c, err := discoveryutils.NewClient(env.ResourceManagerEndpoint, ac, sdc.ProxyURL, proxyAC) + c, err := discoveryutils.NewClient(env.ResourceManagerEndpoint, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create client for %q: %w", env.ResourceManagerEndpoint, err) } @@ -230,7 +230,7 @@ func getRefreshTokenFunc(sdc *SDConfig, ac, proxyAC *promauth.Config, env *cloud return nil, fmt.Errorf("unsupported `authentication_method: %q` only `OAuth` and `ManagedIdentity` are supported", authenticationMethod) } - authClient, err := discoveryutils.NewClient(tokenEndpoint, ac, sdc.ProxyURL, proxyAC) + authClient, err := discoveryutils.NewClient(tokenEndpoint, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot build auth client: %w", err) } diff --git a/lib/promscrape/discovery/azure/machine_test.go b/lib/promscrape/discovery/azure/machine_test.go index f2ba253cad..90141999e5 100644 --- a/lib/promscrape/discovery/azure/machine_test.go +++ b/lib/promscrape/discovery/azure/machine_test.go @@ -66,7 +66,7 @@ func TestGetVirtualMachinesSuccess(t *testing.T) { } })) defer testServer.Close() - c, err := discoveryutils.NewClient(testServer.URL, nil, nil, nil) + c, err := discoveryutils.NewClient(testServer.URL, nil, nil, nil, nil) if err != nil { t.Fatalf("unexpected error at client create: %s", err) } diff --git a/lib/promscrape/discovery/consul/api.go b/lib/promscrape/discovery/consul/api.go index 8ba3b52042..dabd10eadc 100644 --- a/lib/promscrape/discovery/consul/api.go +++ b/lib/promscrape/discovery/consul/api.go @@ -80,7 +80,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } diff --git a/lib/promscrape/discovery/consulagent/api.go b/lib/promscrape/discovery/consulagent/api.go index 8dc706f368..9d5c4761e4 100644 --- a/lib/promscrape/discovery/consulagent/api.go +++ b/lib/promscrape/discovery/consulagent/api.go @@ -74,7 +74,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } diff --git a/lib/promscrape/discovery/digitalocean/api.go b/lib/promscrape/discovery/digitalocean/api.go index b42cc98405..7d4cad4bb2 100644 --- a/lib/promscrape/discovery/digitalocean/api.go +++ b/lib/promscrape/discovery/digitalocean/api.go @@ -36,7 +36,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } @@ -56,7 +56,6 @@ func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { return nil, err } return v.(*apiConfig), nil - } const dropletsAPIPath = "/v2/droplets" diff --git a/lib/promscrape/discovery/docker/api.go b/lib/promscrape/discovery/docker/api.go index e09d063ab9..8301c3fd53 100644 --- a/lib/promscrape/discovery/docker/api.go +++ b/lib/promscrape/discovery/docker/api.go @@ -50,7 +50,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(sdc.Host, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(sdc.Host, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", sdc.Host, err) } diff --git a/lib/promscrape/discovery/dockerswarm/api.go b/lib/promscrape/discovery/dockerswarm/api.go index ebd8e07ed3..160e00d3d7 100644 --- a/lib/promscrape/discovery/dockerswarm/api.go +++ b/lib/promscrape/discovery/dockerswarm/api.go @@ -49,7 +49,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(sdc.Host, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(sdc.Host, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", sdc.Host, err) } diff --git a/lib/promscrape/discovery/eureka/api.go b/lib/promscrape/discovery/eureka/api.go index 93e5e2eeb5..03670870ab 100644 --- a/lib/promscrape/discovery/eureka/api.go +++ b/lib/promscrape/discovery/eureka/api.go @@ -34,7 +34,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } @@ -50,7 +50,6 @@ func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { return nil, err } return v.(*apiConfig), nil - } func getAPIResponse(cfg *apiConfig, path string) ([]byte, error) { diff --git a/lib/promscrape/discovery/http/api.go b/lib/promscrape/discovery/http/api.go index d8ed5adbf2..107167faf6 100644 --- a/lib/promscrape/discovery/http/api.go +++ b/lib/promscrape/discovery/http/api.go @@ -44,7 +44,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } diff --git a/lib/promscrape/discovery/kuma/api.go b/lib/promscrape/discovery/kuma/api.go index f0a9f8096d..c160d15e6a 100644 --- a/lib/promscrape/discovery/kuma/api.go +++ b/lib/promscrape/discovery/kuma/api.go @@ -60,7 +60,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } diff --git a/lib/promscrape/discovery/nomad/api.go b/lib/promscrape/discovery/nomad/api.go index 577bca36ee..33aefa2953 100644 --- a/lib/promscrape/discovery/nomad/api.go +++ b/lib/promscrape/discovery/nomad/api.go @@ -68,7 +68,7 @@ func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) { if err != nil { return nil, fmt.Errorf("cannot parse proxy auth config: %w", err) } - client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC) + client, err := discoveryutils.NewClient(apiServer, ac, sdc.ProxyURL, proxyAC, sdc.HTTPClientConfig.FollowRedirects) if err != nil { return nil, fmt.Errorf("cannot create HTTP client for %q: %w", apiServer, err) } diff --git a/lib/promscrape/discoveryutils/client.go b/lib/promscrape/discoveryutils/client.go index f6dfdcf83a..e9d1bde1f5 100644 --- a/lib/promscrape/discoveryutils/client.go +++ b/lib/promscrape/discoveryutils/client.go @@ -83,7 +83,7 @@ type HTTPClient struct { var defaultDialer = &net.Dialer{} // NewClient returns new Client for the given args. -func NewClient(apiServer string, ac *promauth.Config, proxyURL *proxy.URL, proxyAC *promauth.Config) (*Client, error) { +func NewClient(apiServer string, ac *promauth.Config, proxyURL *proxy.URL, proxyAC *promauth.Config, followRedirects *bool) (*Client, error) { u, err := url.Parse(apiServer) if err != nil { return nil, fmt.Errorf("cannot parse apiServer=%q: %w", apiServer, err) @@ -139,6 +139,14 @@ func NewClient(apiServer string, ac *promauth.Config, proxyURL *proxy.URL, proxy ac.SetHeaders(req, true) } } + if followRedirects != nil && !*followRedirects { + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + blockingClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + } setHTTPProxyHeaders := func(req *http.Request) {} if proxyAC != nil { setHTTPProxyHeaders = func(req *http.Request) { @@ -186,7 +194,8 @@ func (c *Client) GetAPIResponse(path string) ([]byte, error) { } func (c *Client) getAPIResponseWithConcurrencyLimit(ctx context.Context, client *HTTPClient, path string, - modifyRequest RequestCallback, inspectResponse ResponseCallback) ([]byte, error) { + modifyRequest RequestCallback, inspectResponse ResponseCallback, +) ([]byte, error) { // Limit the number of concurrent API requests. concurrencyLimitChOnce.Do(concurrencyLimitChInit) t := timerpool.Get(*maxWaitTime) diff --git a/lib/promscrape/testdata/prometheus.yml b/lib/promscrape/testdata/prometheus.yml index f185bfe6ba..ecae9e7a37 100644 --- a/lib/promscrape/testdata/prometheus.yml +++ b/lib/promscrape/testdata/prometheus.yml @@ -1,5 +1,29 @@ scrape_configs: -- job_name: foo - file_sd_configs: - - files: ["file_sd_*.yml"] - - files: ["file_sd.json"] + - job_name: foo + scrape_interval: 54s + scrape_timeout: 12s + metrics_path: /foo/bar + scheme: https + honor_labels: true + honor_timestamps: false + follow_redirects: false + static_configs: + - targets: ["foo.bar", "aaa"] + labels: + x: y + __scrape_timeout__: "5s" + - job_name: file-job + file_sd_configs: + - files: ["file_sd_*.yml"] + - files: ["file_sd.json"] + - job_name: service-kubernetes + kubernetes_sd_configs: + - role: endpoints + api_server: "https://localhost:1234" + follow_redirects: true + tls_config: + cert_file: valid_cert_file + key_file: valid_key_file + basic_auth: + username: "myusername" + password: "mysecret"