From b14856e512715dcf087069936704125a98b7ec3d Mon Sep 17 00:00:00 2001 From: Povilas Versockas Date: Mon, 18 Nov 2024 10:22:12 -0800 Subject: [PATCH] [receiver/prometheus] Fix static scrape config with Target Allocator (#36063) #### Description Instead of clearing out prometheus scrape configuration I changed the code to copy the initial config every time we sync it from Target Allocator #### Link to tracking issue Fixes https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/36062 and https://github.com/open-telemetry/opentelemetry-operator/issues/3034 #### Testing tested in kind cluster with custom image #### Documentation Co-authored-by: David Ashpole --- .chloggen/ta.yaml | 27 ++++++ .../targetallocator/manager.go | 9 +- .../targetallocator/manager_test.go | 92 +++++++++++++++++++ 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 .chloggen/ta.yaml diff --git a/.chloggen/ta.yaml b/.chloggen/ta.yaml new file mode 100644 index 000000000000..758eb3a761fe --- /dev/null +++ b/.chloggen/ta.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# 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: "Fix prometheus receiver to support static scrape config with Target Allocator" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36062] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# 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: [user] diff --git a/receiver/prometheusreceiver/targetallocator/manager.go b/receiver/prometheusreceiver/targetallocator/manager.go index cd431a3ed72d..d1804bafc630 100644 --- a/receiver/prometheusreceiver/targetallocator/manager.go +++ b/receiver/prometheusreceiver/targetallocator/manager.go @@ -32,6 +32,7 @@ type Manager struct { shutdown chan struct{} cfg *Config promCfg *promconfig.Config + initialScrapeConfigs []*promconfig.ScrapeConfig scrapeManager *scrape.Manager discoveryManager *discovery.Manager enableNativeHistograms bool @@ -43,6 +44,7 @@ func NewManager(set receiver.Settings, cfg *Config, promCfg *promconfig.Config, settings: set, cfg: cfg, promCfg: promCfg, + initialScrapeConfigs: promCfg.ScrapeConfigs, enableNativeHistograms: enableNativeHistograms, } } @@ -115,8 +117,11 @@ func (m *Manager) sync(compareHash uint64, httpClient *http.Client) (uint64, err return hash, nil } - // Clear out the current configurations - m.promCfg.ScrapeConfigs = []*promconfig.ScrapeConfig{} + // Copy initial scrape configurations + initialConfig := make([]*promconfig.ScrapeConfig, len(m.initialScrapeConfigs)) + copy(initialConfig, m.initialScrapeConfigs) + + m.promCfg.ScrapeConfigs = initialConfig for jobName, scrapeConfig := range scrapeConfigsResponse { var httpSD promHTTP.SDConfig diff --git a/receiver/prometheusreceiver/targetallocator/manager_test.go b/receiver/prometheusreceiver/targetallocator/manager_test.go index d0aa13bf44d3..113682bc43f7 100644 --- a/receiver/prometheusreceiver/targetallocator/manager_test.go +++ b/receiver/prometheusreceiver/targetallocator/manager_test.go @@ -833,6 +833,98 @@ func TestConfigureSDHTTPClientConfigFromTA(t *testing.T) { assert.NoError(t, err) } +func TestManagerSyncWithInitialScrapeConfigs(t *testing.T) { + ctx := context.Background() + initialScrapeConfigs := []*promconfig.ScrapeConfig{ + { + JobName: "job1", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + }, + { + JobName: "job2", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + }, + } + + // Mock target allocator response + mockResponse := Responses{ + responses: map[string][]mockTargetAllocatorResponseRaw{ + "/scrape_configs": { + mockTargetAllocatorResponseRaw{code: 200, data: map[string]map[string]any{ + "job1": { + "job_name": "job3", + "scrape_interval": "30s", + "scrape_timeout": "30s", + "scrape_protocols": []string{"OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"}, + "metrics_path": "/metrics", + "scheme": "http", + "relabel_configs": nil, + "metric_relabel_configs": nil, + }, + }}, + }, + "/jobs/job1/targets": { + mockTargetAllocatorResponseRaw{code: 200, data: []hTTPSDResponse{ + {Targets: []string{"localhost:9090", "10.0.10.3:9100", "10.0.10.4:9100", "10.0.10.5:9100"}, + Labels: map[model.LabelName]model.LabelValue{ + "__meta_datacenter": "london", + "__meta_prometheus_job": "node", + }}, + }}, + mockTargetAllocatorResponseRaw{code: 200, data: []hTTPSDResponse{ + {Targets: []string{"localhost:9090", "10.0.10.3:9100", "10.0.10.4:9100", "10.0.10.5:9100"}, + Labels: map[model.LabelName]model.LabelValue{ + "__meta_datacenter": "london", + "__meta_prometheus_job": "node", + }}, + }}, + }, + }, + } + + cfg := &Config{ + Interval: 10 * time.Second, + CollectorID: "collector-1", + HTTPSDConfig: &PromHTTPSDConfig{ + HTTPClientConfig: commonconfig.HTTPClientConfig{}, + RefreshInterval: model.Duration(60 * time.Second), + }, + } + + allocator, err := setupMockTargetAllocator(mockResponse) + require.NoError(t, err, "Failed to create allocator") + + allocator.Start() + defer allocator.Stop() + cfg.Endpoint = allocator.srv.URL // set service URL with the automatic generated one + scrapeManager, discoveryManager := initPrometheusManagers(ctx, t) + + baseCfg := promconfig.Config{GlobalConfig: promconfig.DefaultGlobalConfig, ScrapeConfigs: initialScrapeConfigs} + manager := NewManager(receivertest.NewNopSettings(), cfg, &baseCfg, false) + require.NoError(t, manager.Start(ctx, componenttest.NewNopHost(), scrapeManager, discoveryManager)) + + allocator.wg.Wait() + + providers := discoveryManager.Providers() + + require.NotNil(t, providers) + require.Len(t, providers, 2) + require.IsType(t, &promHTTP.Discovery{}, providers[1].Discoverer()) + + require.Len(t, manager.promCfg.ScrapeConfigs, 3) + require.Equal(t, "job1", manager.promCfg.ScrapeConfigs[0].JobName) + require.Equal(t, "job2", manager.promCfg.ScrapeConfigs[1].JobName) + require.Equal(t, "job3", manager.promCfg.ScrapeConfigs[2].JobName) +} + func initPrometheusManagers(ctx context.Context, t *testing.T) (*scrape.Manager, *discovery.Manager) { logger := log.NewNopLogger() reg := prometheus.NewRegistry()