From 3d56cf033d6901f8d235cf23ed4a4eefeef1e614 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 ++ cmd/otelcontribcol/receivers_test.go | 2 +- .../prometheusexporter/end_to_end_test.go | 3 +- exporter/prometheusexporter/go.mod | 2 +- receiver/prometheusreceiver/config.go | 200 ++++++------------ receiver/prometheusreceiver/config_test.go | 63 ++---- receiver/prometheusreceiver/factory.go | 13 +- .../prometheusreceiver/metrics_receiver.go | 12 +- .../metrics_receiver_helper_test.go | 6 +- .../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 +- receiver/purefareceiver/receiver.go | 2 +- receiver/purefbreceiver/metrics_receiver.go | 2 +- receiver/simpleprometheusreceiver/receiver.go | 2 +- .../simpleprometheusreceiver/receiver_test.go | 14 +- testbed/datareceivers/prometheus.go | 2 +- 19 files changed, 174 insertions(+), 231 deletions(-) create mode 100755 .chloggen/refactorprom.yaml diff --git a/.chloggen/refactorprom.yaml b/.chloggen/refactorprom.yaml new file mode 100755 index 000000000000..ee8e7fe2d3c6 --- /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/cmd/otelcontribcol/receivers_test.go b/cmd/otelcontribcol/receivers_test.go index 144f3f6d9122..37974f35b983 100644 --- a/cmd/otelcontribcol/receivers_test.go +++ b/cmd/otelcontribcol/receivers_test.go @@ -312,7 +312,7 @@ func TestDefaultReceivers(t *testing.T) { receiver: "prometheus", getConfigFn: func() component.Config { cfg := rcvrFactories["prometheus"].CreateDefaultConfig().(*prometheusreceiver.Config) - cfg.PrometheusConfig = &promconfig.Config{ + cfg.PrometheusConfig = &prometheusreceiver.PromConfig{ ScrapeConfigs: []*promconfig.ScrapeConfig{ {JobName: "test"}, }, diff --git a/exporter/prometheusexporter/end_to_end_test.go b/exporter/prometheusexporter/end_to_end_test.go index b4c84b04260f..7cc6b785ecae 100644 --- a/exporter/prometheusexporter/end_to_end_test.go +++ b/exporter/prometheusexporter/end_to_end_test.go @@ -16,7 +16,6 @@ import ( "testing" "time" - promconfig "github.com/prometheus/prometheus/config" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/exporter/exportertest" @@ -88,7 +87,7 @@ func TestEndToEndSummarySupport(t *testing.T) { static_configs: - targets: ['%s'] `, srvURL.Host)) - receiverConfig := new(promconfig.Config) + receiverConfig := new(prometheusreceiver.PromConfig) if err = yaml.Unmarshal(yamlConfig, receiverConfig); err != nil { t.Fatal(err) } diff --git a/exporter/prometheusexporter/go.mod b/exporter/prometheusexporter/go.mod index 6541a09f1cfd..b16efedf8b92 100644 --- a/exporter/prometheusexporter/go.mod +++ b/exporter/prometheusexporter/go.mod @@ -10,7 +10,6 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_model v0.5.0 github.com/prometheus/common v0.46.0 - github.com/prometheus/prometheus v0.48.1 github.com/stretchr/testify v1.8.4 go.opentelemetry.io/collector/component v0.92.1-0.20240112172857-83d463ceba06 go.opentelemetry.io/collector/config/confighttp v0.92.1-0.20240112172857-83d463ceba06 @@ -132,6 +131,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/prometheus v0.48.1 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect github.com/rs/cors v1.10.1 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 // indirect diff --git a/receiver/prometheusreceiver/config.go b/receiver/prometheusreceiver/config.go index eb65a86ab50d..05df61dab858 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 *PromConfig `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,94 +36,74 @@ 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 { - 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:"-"` +// 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 } -var _ component.Config = (*Config)(nil) -var _ confmap.Unmarshaler = (*Config)(nil) - -func checkFile(fn string) error { - // Nothing set, nothing to error on. - if fn == "" { - return nil - } - _, err := os.Stat(fn) - return err +type TargetAllocator struct { + Endpoint string `mapstructure:"endpoint"` + Interval time.Duration `mapstructure:"interval"` + CollectorID string `mapstructure:"collector_id"` + HTTPSDConfig *PromHTTPSDConfig `mapstructure:"http_sd_config"` } -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) +func (cfg *TargetAllocator) Validate() error { + // 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 || len(promConfig.ScrapeConfigs) == 0) && cfg.TargetAllocator == nil { - return errors.New("no Prometheus scrape_configs or target_allocator set") - } +// PromConfig is a redeclaration of promconfig.Config because we need custom unmarshaling +// as prometheus "config" uses `yaml` tags. +type PromConfig promconfig.Config - err := validatePromConfig(promConfig) - if err != nil { - return err - } +var _ confmap.Unmarshaler = (*PromConfig)(nil) - if cfg.TargetAllocator != nil { - err := cfg.validateTargetAllocatorConfig() - if err != nil { - return err - } +func (cfg *PromConfig) Unmarshal(componentParser *confmap.Conf) error { + cfgMap := componentParser.ToStringMap() + if len(cfgMap) == 0 { + return nil } - return nil + return unmarshalYAML(cfgMap, (*promconfig.Config)(cfg)) } -func validatePromConfig(promConfig *promconfig.Config) error { - if promConfig == nil { - return nil - } +func (cfg *PromConfig) 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 { @@ -143,7 +112,7 @@ func validatePromConfig(promConfig *promconfig.Config) error { return fmt.Errorf("unsupported features:\n\t%s", strings.Join(unsupportedFeatures, "\n\t")) } - for _, sc := range promConfig.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) @@ -165,84 +134,49 @@ func validatePromConfig(promConfig *promconfig.Config) error { return nil } -func (cfg *Config) validateTargetAllocatorConfig() error { - // validate targetAllocator - targetAllocatorConfig := cfg.TargetAllocator - if targetAllocatorConfig == nil { - 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") - } +// PromHTTPSDConfig is a redeclaration of promHTTP.SDConfig because we need custom unmarshaling +// as prometheus "config" uses `yaml` tags. +type PromHTTPSDConfig promHTTP.SDConfig - return nil -} +var _ confmap.Unmarshaler = (*PromHTTPSDConfig)(nil) -// Unmarshal a config.Parser into the config struct. -func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error { - if componentParser == nil { +func (cfg *PromHTTPSDConfig) Unmarshal(componentParser *confmap.Conf) error { + cfgMap := componentParser.ToStringMap() + if len(cfgMap) == 0 { return nil } - // We need custom unmarshaling because prometheus "config" subkey defines its own - // YAML unmarshaling routines so we need to do it explicitly. + cfgMap["url"] = "http://placeholder" // we have to set it as else marshaling will fail + return unmarshalYAML(cfgMap, (*promHTTP.SDConfig)(cfg)) +} - err := componentParser.Unmarshal(cfg) +func unmarshalYAML(in map[string]any, out any) error { + 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 647ff8f2c55f..64d53aca5c2f 100644 --- a/receiver/prometheusreceiver/config_test.go +++ b/receiver/prometheusreceiver/config_test.go @@ -189,7 +189,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 @@ -200,7 +200,6 @@ func TestRejectUnsupportedPrometheusFeatures(t *testing.T) { gotErrMsg := strings.ReplaceAll(err.Error(), "\t", strings.Repeat(" ", 8)) require.Equal(t, wantErrMsg, gotErrMsg) - } func TestNonExistentAuthCredentialsFile(t *testing.T) { @@ -213,13 +212,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) { @@ -232,13 +227,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) { @@ -251,13 +242,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) { @@ -269,10 +256,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) { @@ -283,10 +269,9 @@ func TestTLSConfigKeyFileWithoutCertFile(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 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) { @@ -298,10 +283,9 @@ 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) { @@ -314,8 +298,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) { @@ -328,8 +311,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) { @@ -342,6 +324,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 bc6782cab0d2..91eba1919a9b 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,7 +35,7 @@ func NewFactory() receiver.Factory { func createDefaultConfig() component.Config { return &Config{ - PrometheusConfig: &promconfig.Config{ + PrometheusConfig: &PromConfig{ 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 7bce1614604f..2a24fd547211 100644 --- a/receiver/prometheusreceiver/metrics_receiver.go +++ b/receiver/prometheusreceiver/metrics_receiver.go @@ -102,7 +102,7 @@ func (r *pReceiver) Start(_ context.Context, _ component.Host) error { return nil } -func (r *pReceiver) startTargetAllocator(allocConf *targetAllocator, baseCfg *config.Config) error { +func (r *pReceiver) startTargetAllocator(allocConf *TargetAllocator, baseCfg *PromConfig) 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 *PromConfig) (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 *PromConfig) error { + if err := r.scrapeManager.ApplyConfig((*config.Config)(cfg)); err != nil { return err } @@ -291,7 +291,7 @@ func (r *pReceiver) initPrometheusComponents(ctx context.Context, logger log.Log // 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 *PromConfig) 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 56769d375061..105ecc3a0462 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, *PromConfig, 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, (*PromConfig)(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(*PromConfig)) { ctx := context.Background() mp, cfg, err := setupMockPrometheus(targets...) for _, cfgMut := range cfgMuts { diff --git a/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go b/receiver/prometheusreceiver/metrics_receiver_honor_timestamp_test.go index 020ed21db4b9..c42e4f2f9542 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 *PromConfig) { 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 8db248ef0d67..5955b83f94ff 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 *PromConfig) { 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 *PromConfig) { // 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 *PromConfig) { // 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 *PromConfig) { // 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 *PromConfig) { // 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 *PromConfig) { 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 5feed213d1d4..fc52be728465 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: &PromConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &PromHTTPSDConfig{ 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: &PromConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &PromHTTPSDConfig{ 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: &PromConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 10 * time.Second, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &PromHTTPSDConfig{ 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: &PromConfig{}, + TargetAllocator: &TargetAllocator{ Interval: 50 * time.Millisecond, CollectorID: "collector-1", - HTTPSDConfig: &promHTTP.SDConfig{ + HTTPSDConfig: &PromHTTPSDConfig{ 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 bef58d079e7b..44a051df996d 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 *PromConfig want time.Duration }{ { desc: "default", - input: &promConfig.Config{}, + input: &PromConfig{}, want: defaultGCInterval, }, { desc: "global override", - input: &promConfig.Config{ + input: &PromConfig{ 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: &PromConfig{ 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: (*PromConfig)(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 3c030a27978a..ecf7ab8b7ba1 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 *PromConfig) { 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 *PromConfig) { 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 *PromConfig) { 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 *PromConfig) { for _, scrapeConfig := range cfg.ScrapeConfigs { scrapeConfig.MetricRelabelConfigs = []*relabel.Config{ { diff --git a/receiver/purefareceiver/receiver.go b/receiver/purefareceiver/receiver.go index b52e7154411c..076d937cc598 100644 --- a/receiver/purefareceiver/receiver.go +++ b/receiver/purefareceiver/receiver.go @@ -89,7 +89,7 @@ func (r *purefaReceiver) Start(ctx context.Context, compHost component.Host) err } promRecvCfg := fact.CreateDefaultConfig().(*prometheusreceiver.Config) - promRecvCfg.PrometheusConfig = &config.Config{ScrapeConfigs: scrapeCfgs} + promRecvCfg.PrometheusConfig = &prometheusreceiver.PromConfig{ScrapeConfigs: scrapeCfgs} wrapped, err := fact.CreateMetricsReceiver(ctx, r.set, promRecvCfg, r.next) if err != nil { diff --git a/receiver/purefbreceiver/metrics_receiver.go b/receiver/purefbreceiver/metrics_receiver.go index 08c9e63e925d..4ac90459fb62 100644 --- a/receiver/purefbreceiver/metrics_receiver.go +++ b/receiver/purefbreceiver/metrics_receiver.go @@ -65,7 +65,7 @@ func (r *purefbMetricsReceiver) Start(ctx context.Context, compHost component.Ho return err } promRecvCfg := fact.CreateDefaultConfig().(*prometheusreceiver.Config) - promRecvCfg.PrometheusConfig = &config.Config{ScrapeConfigs: scrapeCfgs} + promRecvCfg.PrometheusConfig = &prometheusreceiver.PromConfig{ScrapeConfigs: scrapeCfgs} wrapped, err := fact.CreateMetricsReceiver(ctx, r.set, promRecvCfg, r.next) if err != nil { diff --git a/receiver/simpleprometheusreceiver/receiver.go b/receiver/simpleprometheusreceiver/receiver.go index 1e6f483fd9b7..cf87063caebe 100644 --- a/receiver/simpleprometheusreceiver/receiver.go +++ b/receiver/simpleprometheusreceiver/receiver.go @@ -129,7 +129,7 @@ func getPrometheusConfig(cfg *Config) (*prometheusreceiver.Config, error) { } scrapeConfig.HTTPClientConfig = httpConfig - out.PrometheusConfig = &config.Config{ScrapeConfigs: []*config.ScrapeConfig{ + out.PrometheusConfig = &prometheusreceiver.PromConfig{ScrapeConfigs: []*config.ScrapeConfig{ scrapeConfig, }} diff --git a/receiver/simpleprometheusreceiver/receiver_test.go b/receiver/simpleprometheusreceiver/receiver_test.go index b880214df267..007f85bc8043 100644 --- a/receiver/simpleprometheusreceiver/receiver_test.go +++ b/receiver/simpleprometheusreceiver/receiver_test.go @@ -86,7 +86,7 @@ func TestGetPrometheusConfig(t *testing.T) { Params: url.Values{"foo": []string{"bar", "foobar"}}, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Second), @@ -126,7 +126,7 @@ func TestGetPrometheusConfig(t *testing.T) { MetricsPath: "/metrics", }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { JobName: "prometheus_simple/localhost:1234", @@ -168,7 +168,7 @@ func TestGetPrometheusConfig(t *testing.T) { }, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { JobName: "prometheus_simple/localhost:1234", @@ -230,7 +230,7 @@ func TestGetPrometheusConfigWrapper(t *testing.T) { Params: url.Values{"foo": []string{"bar", "foobar"}}, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Second), @@ -277,7 +277,7 @@ func TestGetPrometheusConfigWrapper(t *testing.T) { Params: url.Values{"foo": []string{"bar", "foobar"}}, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Second), @@ -318,7 +318,7 @@ func TestGetPrometheusConfigWrapper(t *testing.T) { Params: url.Values{"foo": []string{"bar", "foobar"}}, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Second), @@ -362,7 +362,7 @@ func TestGetPrometheusConfigWrapper(t *testing.T) { Params: url.Values{"foo": []string{"bar", "foobar"}}, }, want: &prometheusreceiver.Config{ - PrometheusConfig: &config.Config{ + PrometheusConfig: &prometheusreceiver.PromConfig{ ScrapeConfigs: []*config.ScrapeConfig{ { ScrapeInterval: model.Duration(10 * time.Second), diff --git a/testbed/datareceivers/prometheus.go b/testbed/datareceivers/prometheus.go index 6060fba4f3f5..d979a73ee76c 100644 --- a/testbed/datareceivers/prometheus.go +++ b/testbed/datareceivers/prometheus.go @@ -33,7 +33,7 @@ func (dr *prometheusDataReceiver) Start(_ consumer.Traces, mc consumer.Metrics, factory := prometheusreceiver.NewFactory() cfg := factory.CreateDefaultConfig().(*prometheusreceiver.Config) addr := fmt.Sprintf("127.0.0.1:%d", dr.Port) - cfg.PrometheusConfig = &promconfig.Config{ + cfg.PrometheusConfig = &prometheusreceiver.PromConfig{ ScrapeConfigs: []*promconfig.ScrapeConfig{{ JobName: "testbed-job", ScrapeInterval: model.Duration(100 * time.Millisecond),