From d55ec29c63dce6b0dc3595d498d9a9ba81fc6230 Mon Sep 17 00:00:00 2001 From: Bogdan Drutu Date: Thu, 14 Dec 2023 16:40:33 -0800 Subject: [PATCH] Refactor prometheusreceiver config to avoid some custom unmarshal work Signed-off-by: Bogdan Drutu --- .chloggen/refactorprom.yaml | 22 ++ receiver/prometheusreceiver/config.go | 194 ++++++------------ receiver/prometheusreceiver/config_test.go | 67 +++--- receiver/prometheusreceiver/factory.go | 15 +- .../prometheusreceiver/metrics_receiver.go | 12 +- .../metrics_receiver_helper_test.go | 8 +- .../metrics_receiver_honor_timestamp_test.go | 3 +- .../metrics_receiver_labels_test.go | 13 +- .../metrics_receiver_target_allocator_test.go | 25 ++- .../metrics_receiver_test.go | 10 +- .../metrics_reciever_metric_rename_test.go | 9 +- 11 files changed, 165 insertions(+), 213 deletions(-) create mode 100755 .chloggen/refactorprom.yaml diff --git a/.chloggen/refactorprom.yaml b/.chloggen/refactorprom.yaml new file mode 100755 index 0000000000000..ee8e7fe2d3c64 --- /dev/null +++ b/.chloggen/refactorprom.yaml @@ -0,0 +1,22 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: 'breaking' + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: prometheusreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Consolidate Config members and remove the need of placeholders. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [29901] + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/receiver/prometheusreceiver/config.go b/receiver/prometheusreceiver/config.go index edcd0d2e58614..caff98583b258 100644 --- a/receiver/prometheusreceiver/config.go +++ b/receiver/prometheusreceiver/config.go @@ -16,25 +16,14 @@ import ( promconfig "github.com/prometheus/prometheus/config" promHTTP "github.com/prometheus/prometheus/discovery/http" "github.com/prometheus/prometheus/discovery/kubernetes" - "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" - "go.uber.org/zap" "gopkg.in/yaml.v2" ) -const ( - // The key for Prometheus scraping configs. - prometheusConfigKey = "config" - - // keys to access the http_sd_config from config root - targetAllocatorConfigKey = "target_allocator" - targetAllocatorHTTPSDConfigKey = "http_sd_config" -) - // Config defines configuration for Prometheus receiver. type Config struct { - PrometheusConfig *promconfig.Config `mapstructure:"-"` - TrimMetricSuffixes bool `mapstructure:"trim_metric_suffixes"` + PrometheusConfig *PrometheusConfig `mapstructure:"config"` + TrimMetricSuffixes bool `mapstructure:"trim_metric_suffixes"` // UseStartTimeMetric enables retrieving the start time of all counter metrics // from the process_start_time_seconds metric. This is only correct if all counters on that endpoint // started after the process start time, and the process is the only actor exporting the metric after @@ -47,93 +36,77 @@ type Config struct { // ReportExtraScrapeMetrics - enables reporting of additional metrics for Prometheus client like scrape_body_size_bytes ReportExtraScrapeMetrics bool `mapstructure:"report_extra_scrape_metrics"` - TargetAllocator *targetAllocator `mapstructure:"target_allocator"` - - // ConfigPlaceholder is just an entry to make the configuration pass a check - // that requires that all keys present in the config actually exist on the - // structure, ie.: it will error if an unknown key is present. - ConfigPlaceholder any `mapstructure:"config"` + TargetAllocator *TargetAllocator `mapstructure:"target_allocator"` // EnableProtobufNegotiation allows the collector to set the scraper option for // protobuf negotiation when conferring with a prometheus client. EnableProtobufNegotiation bool `mapstructure:"enable_protobuf_negotiation"` } -type targetAllocator struct { +// Validate checks the receiver configuration is valid. +func (cfg *Config) Validate() error { + if cfg.PrometheusConfig != nil && len(cfg.PrometheusConfig.ScrapeConfigs) == 0 && cfg.TargetAllocator == nil { + return errors.New("no Prometheus scrape_configs or target_allocator set") + } + return nil +} + +type TargetAllocator struct { Endpoint string `mapstructure:"endpoint"` Interval time.Duration `mapstructure:"interval"` CollectorID string `mapstructure:"collector_id"` // ConfigPlaceholder is just an entry to make the configuration pass a check // that requires that all keys present in the config actually exist on the // structure, ie.: it will error if an unknown key is present. - ConfigPlaceholder any `mapstructure:"http_sd_config"` - HTTPSDConfig *promHTTP.SDConfig `mapstructure:"-"` + HTTPSDConfig *HTTPSDConfig `mapstructure:"http_sd_config"` } -var _ component.Config = (*Config)(nil) -var _ confmap.Unmarshaler = (*Config)(nil) - -func checkFile(fn string) error { - // Nothing set, nothing to error on. - if fn == "" { +func (cfg *TargetAllocator) Validate() error { + // validate targetAllocator + if cfg == nil { return nil } - _, err := os.Stat(fn) - return err -} - -func checkTLSConfig(tlsConfig commonconfig.TLSConfig) error { - if err := checkFile(tlsConfig.CertFile); err != nil { - return fmt.Errorf("error checking client cert file %q: %w", tlsConfig.CertFile, err) + // ensure valid endpoint + if _, err := url.ParseRequestURI(cfg.Endpoint); err != nil { + return fmt.Errorf("TargetAllocator endpoint is not valid: %s", cfg.Endpoint) } - if err := checkFile(tlsConfig.KeyFile); err != nil { - return fmt.Errorf("error checking client key file %q: %w", tlsConfig.KeyFile, err) + // ensure valid collectorID without variables + if cfg.CollectorID == "" || strings.Contains(cfg.CollectorID, "${") { + return fmt.Errorf("CollectorID is not a valid ID") } + return nil } -// Validate checks the receiver configuration is valid. -func (cfg *Config) Validate() error { - promConfig := cfg.PrometheusConfig - if promConfig != nil { - err := cfg.validatePromConfig(promConfig) - if err != nil { - return err - } - } +// PrometheusConfig is a redeclaration of the promconfig.Config because we need custom unmarshaling +// because prometheus "config" uses `yaml` tags. +type PrometheusConfig promconfig.Config - if cfg.TargetAllocator != nil { - err := cfg.validateTargetAllocatorConfig() - if err != nil { - return err - } - } - return nil -} +var _ confmap.Unmarshaler = (*PrometheusConfig)(nil) -func (cfg *Config) validatePromConfig(promConfig *promconfig.Config) error { - if len(promConfig.ScrapeConfigs) == 0 && cfg.TargetAllocator == nil { - return errors.New("no Prometheus scrape_configs or target_allocator set") - } +func (cfg *PrometheusConfig) Unmarshal(componentParser *confmap.Conf) error { + return unmarshalYamlStruct(componentParser.ToStringMap(), (*promconfig.Config)(cfg)) +} +func (cfg *PrometheusConfig) Validate() error { // Reject features that Prometheus supports but that the receiver doesn't support: // See: // * https://github.com/open-telemetry/opentelemetry-collector/issues/3863 // * https://github.com/open-telemetry/wg-prometheus/issues/3 unsupportedFeatures := make([]string, 0, 4) - if len(promConfig.RemoteWriteConfigs) != 0 { + if len(cfg.RemoteWriteConfigs) != 0 { unsupportedFeatures = append(unsupportedFeatures, "remote_write") } - if len(promConfig.RemoteReadConfigs) != 0 { + if len(cfg.RemoteReadConfigs) != 0 { unsupportedFeatures = append(unsupportedFeatures, "remote_read") } - if len(promConfig.RuleFiles) != 0 { + if len(cfg.RuleFiles) != 0 { unsupportedFeatures = append(unsupportedFeatures, "rule_files") } - if len(promConfig.AlertingConfig.AlertRelabelConfigs) != 0 { + if len(cfg.AlertingConfig.AlertRelabelConfigs) != 0 { unsupportedFeatures = append(unsupportedFeatures, "alert_config.relabel_configs") } - if len(promConfig.AlertingConfig.AlertmanagerConfigs) != 0 { + if len(cfg.AlertingConfig.AlertmanagerConfigs) != 0 { unsupportedFeatures = append(unsupportedFeatures, "alert_config.alertmanagers") } if len(unsupportedFeatures) != 0 { @@ -142,7 +115,7 @@ func (cfg *Config) validatePromConfig(promConfig *promconfig.Config) error { return fmt.Errorf("unsupported features:\n\t%s", strings.Join(unsupportedFeatures, "\n\t")) } - for _, sc := range cfg.PrometheusConfig.ScrapeConfigs { + for _, sc := range cfg.ScrapeConfigs { if sc.HTTPClientConfig.Authorization != nil { if err := checkFile(sc.HTTPClientConfig.Authorization.CredentialsFile); err != nil { return fmt.Errorf("error checking authorization credentials file %q: %w", sc.HTTPClientConfig.Authorization.CredentialsFile, err) @@ -164,84 +137,53 @@ func (cfg *Config) validatePromConfig(promConfig *promconfig.Config) error { return nil } -func (cfg *Config) validateTargetAllocatorConfig() error { - // validate targetAllocator - targetAllocatorConfig := cfg.TargetAllocator - if targetAllocatorConfig == nil { +// HTTPSDConfig is a redeclaration of the promHTTP.SDConfig because we need custom unmarshaling +// because prometheus "config" uses `yaml` tags. +type HTTPSDConfig promHTTP.SDConfig + +var _ confmap.Unmarshaler = (*HTTPSDConfig)(nil) + +func (cfg *HTTPSDConfig) Unmarshal(componentParser *confmap.Conf) error { + sdConfigMap := componentParser.ToStringMap() + if len(sdConfigMap) == 0 { return nil } - // ensure valid endpoint - if _, err := url.ParseRequestURI(targetAllocatorConfig.Endpoint); err != nil { - return fmt.Errorf("TargetAllocator endpoint is not valid: %s", targetAllocatorConfig.Endpoint) - } - // ensure valid collectorID without variables - if targetAllocatorConfig.CollectorID == "" || strings.Contains(targetAllocatorConfig.CollectorID, "${") { - return fmt.Errorf("CollectorID is not a valid ID") - } - - return nil + sdConfigMap["url"] = "http://placeholder" // we have to set it as else the marshal will fail + return unmarshalYamlStruct(sdConfigMap, (*promHTTP.SDConfig)(cfg)) } -// Unmarshal a config.Parser into the config struct. -func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error { - if componentParser == nil { +func unmarshalYamlStruct(in map[string]any, out any) error { + if len(in) == 0 { return nil } - // We need custom unmarshaling because prometheus "config" subkey defines its own - // YAML unmarshaling routines so we need to do it explicitly. - err := componentParser.Unmarshal(cfg, confmap.WithErrorUnused()) + yamlOut, err := yaml.Marshal(in) if err != nil { - return fmt.Errorf("prometheus receiver failed to parse config: %w", err) + return fmt.Errorf("prometheus receiver: failed to marshal config to yaml: %w", err) } - // Unmarshal prometheus's config values. Since prometheus uses `yaml` tags, so use `yaml`. - promCfg, err := componentParser.Sub(prometheusConfigKey) - if err != nil || len(promCfg.ToStringMap()) == 0 { - return err - } - out, err := yaml.Marshal(promCfg.ToStringMap()) + err = yaml.UnmarshalStrict(yamlOut, out) if err != nil { - return fmt.Errorf("prometheus receiver failed to marshal config to yaml: %w", err) + return fmt.Errorf("prometheus receiver: failed to unmarshal yaml to prometheus config object: %w", err) } + return nil +} - err = yaml.UnmarshalStrict(out, &cfg.PrometheusConfig) - if err != nil { - return fmt.Errorf("prometheus receiver failed to unmarshal yaml to prometheus config: %w", err) +func checkFile(fn string) error { + // Nothing set, nothing to error on. + if fn == "" { + return nil } + _, err := os.Stat(fn) + return err +} - // Unmarshal targetAllocator configs - targetAllocatorCfg, err := componentParser.Sub(targetAllocatorConfigKey) - if err != nil { - return err - } - targetAllocatorHTTPSDCfg, err := targetAllocatorCfg.Sub(targetAllocatorHTTPSDConfigKey) - if err != nil { - return err +func checkTLSConfig(tlsConfig commonconfig.TLSConfig) error { + if err := checkFile(tlsConfig.CertFile); err != nil { + return fmt.Errorf("error checking client cert file %q: %w", tlsConfig.CertFile, err) } - - targetAllocatorHTTPSDMap := targetAllocatorHTTPSDCfg.ToStringMap() - if len(targetAllocatorHTTPSDMap) != 0 { - targetAllocatorHTTPSDMap["url"] = "http://placeholder" // we have to set it as else the marshal will fail - httpSDConf, err := yaml.Marshal(targetAllocatorHTTPSDMap) - if err != nil { - return fmt.Errorf("prometheus receiver failed to marshal config to yaml: %w", err) - } - err = yaml.UnmarshalStrict(httpSDConf, &cfg.TargetAllocator.HTTPSDConfig) - if err != nil { - return fmt.Errorf("prometheus receiver failed to unmarshal yaml to prometheus config: %w", err) - } + if err := checkFile(tlsConfig.KeyFile); err != nil { + return fmt.Errorf("error checking client key file %q: %w", tlsConfig.KeyFile, err) } - return nil } - -func configWarnings(logger *zap.Logger, cfg *Config) { - for _, sc := range cfg.PrometheusConfig.ScrapeConfigs { - for _, rc := range sc.MetricRelabelConfigs { - if rc.TargetLabel == "__name__" { - logger.Warn("metric renaming using metric_relabel_configs will result in unknown-typed metrics without a unit or description", zap.String("job", sc.JobName)) - } - } - } -} diff --git a/receiver/prometheusreceiver/config_test.go b/receiver/prometheusreceiver/config_test.go index 5289ae4c8a5ce..f98faef96728f 100644 --- a/receiver/prometheusreceiver/config_test.go +++ b/receiver/prometheusreceiver/config_test.go @@ -156,7 +156,7 @@ func TestRejectUnsupportedPrometheusFeatures(t *testing.T) { require.NoError(t, component.UnmarshalConfig(sub, cfg)) err = component.ValidateConfig(cfg) - require.NotNil(t, err, "Expected a non-nil error") + require.Error(t, err) wantErrMsg := `unsupported features: alert_config.alertmanagers @@ -166,8 +166,7 @@ func TestRejectUnsupportedPrometheusFeatures(t *testing.T) { rule_files` gotErrMsg := strings.ReplaceAll(err.Error(), "\t", strings.Repeat(" ", 8)) - require.Equal(t, wantErrMsg, gotErrMsg) - + require.Equal(t, gotErrMsg, wantErrMsg) } func TestNonExistentAuthCredentialsFile(t *testing.T) { @@ -180,13 +179,9 @@ func TestNonExistentAuthCredentialsFile(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NotNil(t, err, "Expected a non-nil error") - - wantErrMsg := `error checking authorization credentials file "/nonexistentauthcredentialsfile"` - - gotErrMsg := err.Error() - require.True(t, strings.HasPrefix(gotErrMsg, wantErrMsg)) + assert.ErrorContains(t, + component.ValidateConfig(cfg), + `error checking authorization credentials file "/nonexistentauthcredentialsfile"`) } func TestTLSConfigNonExistentCertFile(t *testing.T) { @@ -199,13 +194,9 @@ func TestTLSConfigNonExistentCertFile(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NotNil(t, err, "Expected a non-nil error") - - wantErrMsg := `error checking client cert file "/nonexistentcertfile"` - - gotErrMsg := err.Error() - require.True(t, strings.HasPrefix(gotErrMsg, wantErrMsg)) + assert.ErrorContains(t, + component.ValidateConfig(cfg), + `error checking client cert file "/nonexistentcertfile"`) } func TestTLSConfigNonExistentKeyFile(t *testing.T) { @@ -218,13 +209,9 @@ func TestTLSConfigNonExistentKeyFile(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NotNil(t, err, "Expected a non-nil error") - - wantErrMsg := `error checking client key file "/nonexistentkeyfile"` - - gotErrMsg := err.Error() - require.True(t, strings.HasPrefix(gotErrMsg, wantErrMsg)) + assert.ErrorContains(t, + component.ValidateConfig(cfg), + `error checking client key file "/nonexistentkeyfile"`) } func TestTLSConfigCertFileWithoutKeyFile(t *testing.T) { @@ -236,10 +223,9 @@ func TestTLSConfigCertFileWithoutKeyFile(t *testing.T) { sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String()) require.NoError(t, err) - err = component.UnmarshalConfig(sub, cfg) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "exactly one of key or key_file must be configured when a client certificate is configured") - } + assert.ErrorContains(t, + component.UnmarshalConfig(sub, cfg), + "exactly one of key or key_file must be configured when a client certificate is configured") } func TestTLSConfigKeyFileWithoutCertFile(t *testing.T) { @@ -249,11 +235,9 @@ func TestTLSConfigKeyFileWithoutCertFile(t *testing.T) { cfg := factory.CreateDefaultConfig() sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String()) - require.NoError(t, err) - err = component.UnmarshalConfig(sub, cfg) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "exactly one of cert or cert_file must be configured when a client key is configured") - } + assert.ErrorContains(t, + component.UnmarshalConfig(sub, cfg), + "exactly one of cert or cert_file must be configured when a client key is configured") } func TestKubernetesSDConfigWithoutKeyFile(t *testing.T) { @@ -265,10 +249,10 @@ func TestKubernetesSDConfigWithoutKeyFile(t *testing.T) { sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String()) require.NoError(t, err) - err = component.UnmarshalConfig(sub, cfg) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "exactly one of key or key_file must be configured when a client certificate is configured") - } + assert.ErrorContains(t, + component.UnmarshalConfig(sub, cfg), + + "exactly one of key or key_file must be configured when a client certificate is configured") } func TestFileSDConfigJsonNilTargetGroup(t *testing.T) { @@ -281,8 +265,7 @@ func TestFileSDConfigJsonNilTargetGroup(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NoError(t, err) + require.NoError(t, component.ValidateConfig(cfg)) } func TestFileSDConfigYamlNilTargetGroup(t *testing.T) { @@ -295,8 +278,7 @@ func TestFileSDConfigYamlNilTargetGroup(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NoError(t, err) + require.NoError(t, component.ValidateConfig(cfg)) } func TestFileSDConfigWithoutSDFile(t *testing.T) { @@ -309,6 +291,5 @@ func TestFileSDConfigWithoutSDFile(t *testing.T) { require.NoError(t, err) require.NoError(t, component.UnmarshalConfig(sub, cfg)) - err = component.ValidateConfig(cfg) - require.NoError(t, err) + require.NoError(t, component.ValidateConfig(cfg)) } diff --git a/receiver/prometheusreceiver/factory.go b/receiver/prometheusreceiver/factory.go index bc6782cab0d28..b4dbd35598f58 100644 --- a/receiver/prometheusreceiver/factory.go +++ b/receiver/prometheusreceiver/factory.go @@ -12,6 +12,7 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/receiver" + "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver/internal/metadata" ) @@ -34,9 +35,9 @@ func NewFactory() receiver.Factory { func createDefaultConfig() component.Config { return &Config{ - PrometheusConfig: &promconfig.Config{ + PrometheusConfig: (*PrometheusConfig)(&promconfig.Config{ GlobalConfig: promconfig.DefaultGlobalConfig, - }, + }), } } @@ -49,3 +50,13 @@ func createMetricsReceiver( configWarnings(set.Logger, cfg.(*Config)) return newPrometheusReceiver(set, cfg.(*Config), nextConsumer), nil } + +func configWarnings(logger *zap.Logger, cfg *Config) { + for _, sc := range cfg.PrometheusConfig.ScrapeConfigs { + for _, rc := range sc.MetricRelabelConfigs { + if rc.TargetLabel == "__name__" { + logger.Warn("metric renaming using metric_relabel_configs will result in unknown-typed metrics without a unit or description", zap.String("job", sc.JobName)) + } + } + } +} diff --git a/receiver/prometheusreceiver/metrics_receiver.go b/receiver/prometheusreceiver/metrics_receiver.go index c8d709b4ca193..3a5e87b10ef2b 100644 --- a/receiver/prometheusreceiver/metrics_receiver.go +++ b/receiver/prometheusreceiver/metrics_receiver.go @@ -102,7 +102,7 @@ func (r *pReceiver) Start(_ context.Context, host component.Host) error { return nil } -func (r *pReceiver) startTargetAllocator(allocConf *targetAllocator, baseCfg *config.Config) error { +func (r *pReceiver) startTargetAllocator(allocConf *TargetAllocator, baseCfg *PrometheusConfig) error { r.settings.Logger.Info("Starting target allocator discovery") // immediately sync jobs, not waiting for the first tick savedHash, err := r.syncTargetAllocator(uint64(0), allocConf, baseCfg) @@ -132,7 +132,7 @@ func (r *pReceiver) startTargetAllocator(allocConf *targetAllocator, baseCfg *co // syncTargetAllocator request jobs from targetAllocator and update underlying receiver, if the response does not match the provided compareHash. // baseDiscoveryCfg can be used to provide additional ScrapeConfigs which will be added to the retrieved jobs. -func (r *pReceiver) syncTargetAllocator(compareHash uint64, allocConf *targetAllocator, baseCfg *config.Config) (uint64, error) { +func (r *pReceiver) syncTargetAllocator(compareHash uint64, allocConf *TargetAllocator, baseCfg *PrometheusConfig) (uint64, error) { r.settings.Logger.Debug("Syncing target allocator jobs") scrapeConfigsResponse, err := r.getScrapeConfigsResponse(allocConf.Endpoint) if err != nil { @@ -160,7 +160,7 @@ func (r *pReceiver) syncTargetAllocator(compareHash uint64, allocConf *targetAll RefreshInterval: model.Duration(30 * time.Second), } } else { - httpSD = *allocConf.HTTPSDConfig + httpSD = promHTTP.SDConfig(*allocConf.HTTPSDConfig) } escapedJob := url.QueryEscape(jobName) httpSD.URL = fmt.Sprintf("%s/jobs/%s/targets?collector_id=%s", allocConf.Endpoint, escapedJob, allocConf.CollectorID) @@ -220,8 +220,8 @@ func (r *pReceiver) getScrapeConfigsResponse(baseURL string) (map[string]*config return jobToScrapeConfig, nil } -func (r *pReceiver) applyCfg(cfg *config.Config) error { - if err := r.scrapeManager.ApplyConfig(cfg); err != nil { +func (r *pReceiver) applyCfg(cfg *PrometheusConfig) error { + if err := r.scrapeManager.ApplyConfig((*config.Config)(cfg)); err != nil { return err } @@ -291,7 +291,7 @@ func (r *pReceiver) initPrometheusComponents(ctx context.Context, host component // gcInterval returns the longest scrape interval used by a scrape config, // plus a delta to prevent race conditions. // This ensures jobs are not garbage collected between scrapes. -func gcInterval(cfg *config.Config) time.Duration { +func gcInterval(cfg *PrometheusConfig) time.Duration { gcInterval := defaultGCInterval if time.Duration(cfg.GlobalConfig.ScrapeInterval)+gcIntervalDelta > gcInterval { gcInterval = time.Duration(cfg.GlobalConfig.ScrapeInterval) + gcIntervalDelta diff --git a/receiver/prometheusreceiver/metrics_receiver_helper_test.go b/receiver/prometheusreceiver/metrics_receiver_helper_test.go index 56769d3750615..c535958a76717 100644 --- a/receiver/prometheusreceiver/metrics_receiver_helper_test.go +++ b/receiver/prometheusreceiver/metrics_receiver_helper_test.go @@ -128,7 +128,7 @@ type testData struct { // setupMockPrometheus to create a mocked prometheus based on targets, returning the server and a prometheus exporting // config -func setupMockPrometheus(tds ...*testData) (*mockPrometheus, *promcfg.Config, error) { +func setupMockPrometheus(tds ...*testData) (*mockPrometheus, *PrometheusConfig, error) { jobs := make([]map[string]any, 0, len(tds)) endpoints := make(map[string][]mockPrometheusResponse) metricPaths := make([]string, len(tds)) @@ -163,7 +163,7 @@ func setupMockPrometheus(tds ...*testData) (*mockPrometheus, *promcfg.Config, er t.attributes = internal.CreateResource(t.name, u.Host, l).Attributes() } pCfg, err := promcfg.Load(string(cfg), false, gokitlog.NewNopLogger()) - return mp, pCfg, err + return mp, (*PrometheusConfig)(pCfg), err } func waitForScrapeResults(t *testing.T, targets []*testData, cms *consumertest.MetricsSink) { @@ -608,7 +608,7 @@ func compareSummary(count uint64, sum float64, quantiles [][]float64) summaryPoi } // starts prometheus receiver with custom config, retrieves metrics from MetricsSink -func testComponent(t *testing.T, targets []*testData, alterConfig func(*Config), cfgMuts ...func(*promcfg.Config)) { +func testComponent(t *testing.T, targets []*testData, alterConfig func(*Config), cfgMuts ...func(*PrometheusConfig)) { ctx := context.Background() mp, cfg, err := setupMockPrometheus(targets...) for _, cfgMut := range cfgMuts { @@ -618,7 +618,7 @@ func testComponent(t *testing.T, targets []*testData, alterConfig func(*Config), defer mp.Close() config := &Config{ - PrometheusConfig: cfg, + PrometheusConfig: (*PrometheusConfig)(cfg), StartTimeMetricRegex: "", } if alterConfig != nil { diff --git a/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go b/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go index 020ed21db4b9f..5b7b0ef8a573e 100644 --- a/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go +++ b/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - promcfg "github.com/prometheus/prometheus/config" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" @@ -168,7 +167,7 @@ func TestHonorTimeStampsWithFalse(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.HonorTimestamps = false } diff --git a/receiver/prometheusreceiver/metrics_receiver_labels_test.go b/receiver/prometheusreceiver/metrics_receiver_labels_test.go index 8db248ef0d67c..81486f483c160 100644 --- a/receiver/prometheusreceiver/metrics_receiver_labels_test.go +++ b/receiver/prometheusreceiver/metrics_receiver_labels_test.go @@ -6,7 +6,6 @@ package prometheusreceiver import ( "testing" - promcfg "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/relabel" "github.com/stretchr/testify/require" @@ -29,7 +28,7 @@ func TestExternalLabels(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { cfg.GlobalConfig.ExternalLabels = labels.FromStrings("key", "value") }) } @@ -121,7 +120,7 @@ func TestLabelLimitConfig(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { // set label limit in scrape_config for _, scrapeCfg := range cfg.ScrapeConfigs { scrapeCfg.LabelLimit = 5 @@ -248,7 +247,7 @@ func TestLabelNameLimitConfig(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { // set label limit in scrape_config for _, scrapeCfg := range cfg.ScrapeConfigs { scrapeCfg.LabelNameLengthLimit = 20 @@ -284,7 +283,7 @@ func TestLabelValueLimitConfig(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { // set label name limit in scrape_config for _, scrapeCfg := range cfg.ScrapeConfigs { scrapeCfg.LabelValueLengthLimit = 25 @@ -613,7 +612,7 @@ func TestHonorLabelsTrueConfig(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { // set label name limit in scrape_config for _, scrapeCfg := range cfg.ScrapeConfigs { scrapeCfg.HonorLabels = true @@ -639,7 +638,7 @@ func TestRelabelJobInstance(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ { diff --git a/receiver/prometheusreceiver/metrics_receiver_target_allocator_test.go b/receiver/prometheusreceiver/metrics_receiver_target_allocator_test.go index 5feed213d1d44..dd9cff6d98ad5 100644 --- a/receiver/prometheusreceiver/metrics_receiver_target_allocator_test.go +++ b/receiver/prometheusreceiver/metrics_receiver_target_allocator_test.go @@ -18,7 +18,6 @@ import ( commonconfig "github.com/prometheus/common/config" "github.com/prometheus/common/model" - promConfig "github.com/prometheus/prometheus/config" promHTTP "github.com/prometheus/prometheus/discovery/http" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" @@ -218,11 +217,11 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) { }, }, cfg: &Config{ - PrometheusConfig: &promConfig.Config{}, - TargetAllocator: &targetAllocator{ + PrometheusConfig: &PrometheusConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &HTTPSDConfig{ HTTPClientConfig: commonconfig.HTTPClientConfig{ BasicAuth: &commonconfig.BasicAuth{ Username: "user", @@ -312,11 +311,11 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) { }, }, cfg: &Config{ - PrometheusConfig: &promConfig.Config{}, - TargetAllocator: &targetAllocator{ + PrometheusConfig: &PrometheusConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &HTTPSDConfig{ HTTPClientConfig: commonconfig.HTTPClientConfig{}, RefreshInterval: model.Duration(60 * time.Second), }, @@ -424,11 +423,11 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) { }, }, cfg: &Config{ - PrometheusConfig: &promConfig.Config{}, - TargetAllocator: &targetAllocator{ + PrometheusConfig: &PrometheusConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &HTTPSDConfig{ HTTPClientConfig: commonconfig.HTTPClientConfig{}, RefreshInterval: model.Duration(60 * time.Second), }, @@ -466,11 +465,11 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) { }, }, cfg: &Config{ - PrometheusConfig: &promConfig.Config{}, - TargetAllocator: &targetAllocator{ + PrometheusConfig: &PrometheusConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 50 * time.Millisecond, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &HTTPSDConfig{ HTTPClientConfig: commonconfig.HTTPClientConfig{}, RefreshInterval: model.Duration(60 * time.Second), }, diff --git a/receiver/prometheusreceiver/metrics_receiver_test.go b/receiver/prometheusreceiver/metrics_receiver_test.go index bef58d079e7bd..bff50fdd1f027 100644 --- a/receiver/prometheusreceiver/metrics_receiver_test.go +++ b/receiver/prometheusreceiver/metrics_receiver_test.go @@ -1566,17 +1566,17 @@ func verifyUntypedMetrics(t *testing.T, td *testData, resourceMetrics []pmetric. func TestGCInterval(t *testing.T) { for _, tc := range []struct { desc string - input *promConfig.Config + input *PrometheusConfig want time.Duration }{ { desc: "default", - input: &promConfig.Config{}, + input: &PrometheusConfig{}, want: defaultGCInterval, }, { desc: "global override", - input: &promConfig.Config{ + input: &PrometheusConfig{ GlobalConfig: promConfig.GlobalConfig{ ScrapeInterval: model.Duration(10 * time.Minute), }, @@ -1585,7 +1585,7 @@ func TestGCInterval(t *testing.T) { }, { desc: "scrape config override", - input: &promConfig.Config{ + input: &PrometheusConfig{ ScrapeConfigs: []*promConfig.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Minute), @@ -1625,7 +1625,7 @@ scrape_configs: require.NoError(t, err) set := receivertest.NewNopCreateSettings() receiver := newPrometheusReceiver(set, &Config{ - PrometheusConfig: cfg, + PrometheusConfig: (*PrometheusConfig)(cfg), }, new(consumertest.MetricsSink)) ctx := context.Background() diff --git a/receiver/prometheusreceiver/metrics_reciever_metric_rename_test.go b/receiver/prometheusreceiver/metrics_reciever_metric_rename_test.go index 3c030a27978a2..df9c4355247f3 100644 --- a/receiver/prometheusreceiver/metrics_reciever_metric_rename_test.go +++ b/receiver/prometheusreceiver/metrics_reciever_metric_rename_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/prometheus/common/model" - promcfg "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/relabel" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/pdata/pmetric" @@ -47,7 +46,7 @@ func TestMetricRenaming(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ { @@ -90,7 +89,7 @@ func TestMetricRenamingKeepAction(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ { @@ -232,7 +231,7 @@ func TestLabelRenaming(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ { @@ -362,7 +361,7 @@ func TestLabelRenamingKeepAction(t *testing.T) { }, } - testComponent(t, targets, nil, func(cfg *promcfg.Config) { + testComponent(t, targets, nil, func(cfg *PrometheusConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ {