Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for - syncTargetAllocator in prometheus metrics receiver doesnt detect regex changes in metrics relabel config #30258

Closed
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d1de8b3
Adding more validation
rashmichandrashekar Aug 28, 2021
35f4b63
auth test
rashmichandrashekar Aug 31, 2021
d3b0ccd
adding more tests
rashmichandrashekar Aug 31, 2021
f631611
kubernetes sd test
rashmichandrashekar Aug 31, 2021
11e35c0
adding more tests
rashmichandrashekar Aug 31, 2021
0a886f4
Merge pull request #1 from rashmichandrashekar/rashmi/promreceiver-co…
rashmichandrashekar Aug 31, 2021
9d9a83c
addressing PR comments
rashmichandrashekar Sep 8, 2021
fe3b4f2
Merge pull request #2 from rashmichandrashekar/rashmi/pr-comments
rashmichandrashekar Sep 8, 2021
a2fa93e
Merge pull request #3 from rashmichandrashekar/rashmi/promreceiver-co…
rashmichandrashekar Sep 8, 2021
f5e999d
fixing PR comment related to returning error when file doesnt exist i…
rashmichandrashekar Sep 8, 2021
424be70
Merge pull request #4 from rashmichandrashekar/rashmi/pr-comments
rashmichandrashekar Sep 8, 2021
fd262e9
Merge pull request #5 from rashmichandrashekar/rashmi/pr-comments
rashmichandrashekar Sep 8, 2021
fff66e3
Fixing lint errors
rashmichandrashekar Sep 9, 2021
f47c4ff
Merge pull request #6 from rashmichandrashekar/rashmi/pr-comments
rashmichandrashekar Sep 9, 2021
affec96
Merge pull request #7 from rashmichandrashekar/rashmi/promreceiver-co…
rashmichandrashekar Sep 9, 2021
eebe192
merging with remote
rashmichandrashekar Oct 5, 2021
a4080d8
Merge branch 'main' of https://github.com/rashmichandrashekar/opentel…
rashmichandrashekar Oct 5, 2021
f17fbd0
Merge remote-tracking branch 'upstream/main'
rashmichandrashekar Jan 2, 2024
aa69f7f
changes to use yaml encoding for hashing
rashmichandrashekar Jan 2, 2024
dd00682
adding unit test for regex update
rashmichandrashekar Jan 6, 2024
4015b6b
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 6, 2024
b3d8c53
add change log
rashmichandrashekar Jan 6, 2024
7e40ef5
Merge branch 'rashmi/hash-fix' of https://github.com/rashmichandrashe…
rashmichandrashekar Jan 6, 2024
fa2d692
running go mod tidy
rashmichandrashekar Jan 7, 2024
fc4a588
go mod tidy on receiver
rashmichandrashekar Jan 7, 2024
955d7ff
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 8, 2024
6211f40
running make gotidy
rashmichandrashekar Jan 8, 2024
2f9e8c8
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 8, 2024
a918c4d
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 9, 2024
dca8ecd
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 10, 2024
7f45edb
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 10, 2024
3e8d1ac
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 11, 2024
384f805
Merge branch 'main' into rashmi/hash-fix
fatsheep9146 Jan 11, 2024
8c57a93
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 11, 2024
4a2030c
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 11, 2024
44c67d8
merging with upstream/main
rashmichandrashekar Jan 19, 2024
623c161
using structhash package
rashmichandrashekar Jan 19, 2024
fb72c64
updating go mod and go sum
rashmichandrashekar Jan 19, 2024
db313a7
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 23, 2024
16ee77b
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 23, 2024
899d093
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 24, 2024
289e7ea
Adding benchmarks
rashmichandrashekar Jan 25, 2024
874cb30
Merge branch 'rashmi/hash-fix' of https://github.com/rashmichandrashe…
rashmichandrashekar Jan 25, 2024
6325728
adding ref
rashmichandrashekar Jan 25, 2024
336d87a
bug fix
rashmichandrashekar Jan 25, 2024
8284879
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 25, 2024
b5dfef6
updating go mod
rashmichandrashekar Jan 25, 2024
84d438b
Merge branch 'rashmi/hash-fix' of https://github.com/rashmichandrashe…
rashmichandrashekar Jan 25, 2024
f3f7577
adding missing entries
rashmichandrashekar Jan 25, 2024
dc001e7
make gotidy
rashmichandrashekar Jan 25, 2024
cd7cbc9
Merge branch 'main' into rashmi/hash-fix
rashmichandrashekar Jan 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .chloggen/fix_promTAHashComputation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
change_type: 'bug_fix'
component: 'prometheusreceiver'
note: Fix hash computation to include non exported fields like regex in scrape configuration for TargetAllocator
issues: [29313]
subtext:
change_logs: []
1 change: 0 additions & 1 deletion cmd/configschema/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,6 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
Expand Down
2 changes: 0 additions & 2 deletions cmd/configschema/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion cmd/otelcontribcol/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,6 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
Expand Down
2 changes: 0 additions & 2 deletions cmd/otelcontribcol/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion cmd/oteltestbedcol/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ require (
github.com/miekg/dns v1.1.56 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
2 changes: 0 additions & 2 deletions cmd/oteltestbedcol/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion exporter/datadogexporter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ require (
github.com/miekg/dns v1.1.56 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
2 changes: 0 additions & 2 deletions exporter/datadogexporter/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion exporter/datadogexporter/integrationtest/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion exporter/prometheusexporter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ require (
github.com/miekg/dns v1.1.56 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
2 changes: 0 additions & 2 deletions exporter/prometheusexporter/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,6 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion receiver/prometheusreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require (
github.com/go-kit/log v0.2.1
github.com/gogo/protobuf v1.3.2
github.com/golang/snappy v0.0.4
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter v0.92.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.92.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.92.0
Expand Down
2 changes: 0 additions & 2 deletions receiver/prometheusreceiver/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 28 additions & 7 deletions receiver/prometheusreceiver/metrics_receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"context"
"errors"
"fmt"
"hash"
"hash/fnv"
"io"
"net/http"
"net/url"
Expand All @@ -17,7 +19,6 @@ import (
"time"

"github.com/go-kit/log"
"github.com/mitchellh/hashstructure/v2"
commonconfig "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
Expand Down Expand Up @@ -105,7 +106,7 @@ func (r *pReceiver) Start(_ context.Context, host component.Host) error {
func (r *pReceiver) startTargetAllocator(allocConf *targetAllocator, baseCfg *config.Config) 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)
savedHash, err := r.syncTargetAllocator(nil, allocConf, baseCfg)
if err != nil {
return err
}
Expand All @@ -130,20 +131,40 @@ func (r *pReceiver) startTargetAllocator(allocConf *targetAllocator, baseCfg *co
return nil
}

// Calculate a hash for a scrape config map.
// This is done by marshaling to YAML because it's the most straightforward and doesn't run into problems with unexported fields.
func getScrapeConfigHash(jobToScrapeConfig map[string]*config.ScrapeConfig) (hash.Hash64, error) {
var err error
hash := fnv.New64()
yamlEncoder := yaml.NewEncoder(hash)
for jobName, scrapeConfig := range jobToScrapeConfig {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next.

I don't think this is going to be a viable approach. To ensure consistency I believe it necessary to first extract the keys from the map into a slice and sort them, then iterate over the sorted slice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aneurysm9 - I did find another way to fix this issue using a different package. Please lmk if you see any concerns.

Copy link
Contributor Author

@rashmichandrashekar rashmichandrashekar Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aneurysm9 - here are the results for the benchmark ( see the test for the benchmark tests i used. This is just for reference and I can remove them after you review)
image

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Aneurysm9, Following up. Please lmk if you have any suggestions/feedback. Thanks for looking into this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aneurysm9 - Following up. Please lmk if you have any concerns/feedback. Thanks!

_, err = hash.Write([]byte(jobName))
if err != nil {
return nil, err
}
err = yamlEncoder.Encode(scrapeConfig)
if err != nil {
return nil, err
}
}
yamlEncoder.Close()
return hash, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return hash, err
return hash.Sum64(), err

I don't think anything outside of this function needs access to the mutable hash.Hash instance, so returning the uint64 sum can reduce the knock-on effects on other methods where types are changed.

}

// 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 hash.Hash64, allocConf *targetAllocator, baseCfg *config.Config) (hash.Hash64, error) {
r.settings.Logger.Debug("Syncing target allocator jobs")
scrapeConfigsResponse, err := r.getScrapeConfigsResponse(allocConf.Endpoint)
if err != nil {
r.settings.Logger.Error("Failed to retrieve job list", zap.Error(err))
return 0, err
return nil, err
}

hash, err := hashstructure.Hash(scrapeConfigsResponse, hashstructure.FormatV2, nil)
hash, err := getScrapeConfigHash(scrapeConfigsResponse)
if err != nil {
r.settings.Logger.Error("Failed to hash job list", zap.Error(err))
return 0, err
return nil, err
}
if hash == compareHash {
// no update needed
Expand Down Expand Up @@ -175,7 +196,7 @@ func (r *pReceiver) syncTargetAllocator(compareHash uint64, allocConf *targetAll
err = r.applyCfg(baseCfg)
if err != nil {
r.settings.Logger.Error("Failed to apply new scrape configuration", zap.Error(err))
return 0, err
return nil, err
}

return hash, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/prometheus/common/model"
promConfig "github.com/prometheus/prometheus/config"
promHTTP "github.com/prometheus/prometheus/discovery/http"
"github.com/prometheus/prometheus/model/relabel"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/consumer/consumertest"
Expand Down Expand Up @@ -50,9 +51,15 @@ type hTTPSDResponse struct {
Labels map[model.LabelName]model.LabelValue `json:"labels"`
}

type expectedMetricRelabelConfigTestResult struct {
JobName string
MetricRelabelRegex relabel.Regexp
}

type expectedTestResultJobMap struct {
Targets []string
Labels model.LabelSet
Targets []string
Labels model.LabelSet
MetricRelabelConfig *expectedMetricRelabelConfigTestResult
}

type expectedTestResult struct {
Expand Down Expand Up @@ -111,7 +118,6 @@ func transformTAResponseMap(rawResponses map[string][]mockTargetAllocatorRespons
})
}
responsesMap[path] = responses

v := &atomic.Int32{}
responsesIndexMap[path] = v
}
Expand Down Expand Up @@ -481,6 +487,111 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) {
jobMap: map[string]expectedTestResultJobMap{},
},
},
{
desc: "update metric relabel config regex",
responses: Responses{
releaserMap: map[string]int{
"/scrape_configs": 1,
},
responses: map[string][]mockTargetAllocatorResponseRaw{
"/scrape_configs": {
mockTargetAllocatorResponseRaw{code: 200, data: map[string]map[string]any{
"job1": {
"job_name": "job1",
"scrape_interval": "30s",
"scrape_timeout": "30s",
"metrics_path": "/metrics",
"scheme": "http",
"metric_relabel_configs": []map[string]string{
{
"separator": ";",
"regex": "regex1",
"action": "keep",
},
},
},
}},
mockTargetAllocatorResponseRaw{code: 200, data: map[string]map[string]any{
"job1": {
"job_name": "job1",
"scrape_interval": "30s",
"scrape_timeout": "30s",
"metrics_path": "/metrics",
"scheme": "http",
"metric_relabel_configs": []map[string]string{
{
"separator": ";",
"regex": "regex2",
"action": "keep",
},
},
},
}},
},
"/jobs/job1/targets": {
mockTargetAllocatorResponseRaw{code: 200, data: []hTTPSDResponse{
{Targets: []string{"localhost:9090"},
Labels: map[model.LabelName]model.LabelValue{
"__meta_datacenter": "london",
"__meta_prometheus_job": "node",
}},
}},
mockTargetAllocatorResponseRaw{code: 200, data: []hTTPSDResponse{
{Targets: []string{"localhost:9090"},
Labels: map[model.LabelName]model.LabelValue{
"__meta_datacenter": "london",
"__meta_prometheus_job": "node",
}},
}},
},
},
},
cfg: &Config{
PrometheusConfig: &promConfig.Config{
ScrapeConfigs: []*promConfig.ScrapeConfig{
{
JobName: "job1",
HonorTimestamps: true,
ScrapeInterval: model.Duration(30 * time.Second),
ScrapeTimeout: model.Duration(30 * time.Second),
MetricsPath: "/metrics",
Scheme: "http",
MetricRelabelConfigs: []*relabel.Config{
{
Separator: ";",
Regex: relabel.MustNewRegexp("(.*)"),
Action: relabel.Keep,
},
},
},
},
},
TargetAllocator: &targetAllocator{
Interval: 10 * time.Second,
CollectorID: "collector-1",
HTTPSDConfig: &promHTTP.SDConfig{
HTTPClientConfig: commonconfig.HTTPClientConfig{},
RefreshInterval: model.Duration(60 * time.Second),
},
},
},
want: expectedTestResult{
empty: false,
jobMap: map[string]expectedTestResultJobMap{
"job1": {
Targets: []string{"localhost:9090"},
Labels: map[model.LabelName]model.LabelValue{
"__meta_datacenter": "london",
"__meta_prometheus_job": "node",
},
MetricRelabelConfig: &expectedMetricRelabelConfigTestResult{
JobName: "job1",
MetricRelabelRegex: relabel.MustNewRegexp("regex2"),
},
},
},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
ctx := context.Background()
Expand All @@ -505,7 +616,6 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) {
require.Len(t, providers, 0)
return
}

require.NotNil(t, providers)

for _, provider := range providers {
Expand All @@ -532,10 +642,23 @@ func TestTargetAllocatorJobRetrieval(t *testing.T) {
// which is identical to the source url
s.Labels["__meta_url"] = model.LabelValue(sdConfig.URL)
require.Equal(t, s.Labels, group.Labels)

if s.MetricRelabelConfig != nil {
// Adding wait here so that the latest scrape config is applied with the updated regex
time.Sleep(5 * time.Second)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting a 5s sleep inside a triply-nested loop can have a significant impact on test runtime. Can this be avoided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aneurysm9 - Unfortunately, looks like if the sleep is not present, the updated regex doesnt get picked up. This is the only test that uses the sleep and for all the other tests it wouldn't go in this loop.

for _, sc := range receiver.cfg.PrometheusConfig.ScrapeConfigs {
if sc.JobName == s.MetricRelabelConfig.JobName {
for _, mc := range sc.MetricRelabelConfigs {
require.Equal(t, s.MetricRelabelConfig.MetricRelabelRegex, mc.Regex)
}
}
}
}
found = true
}
require.True(t, found, "Returned job is not defined in expected values", group)
}

}
})
}
Expand Down
1 change: 0 additions & 1 deletion receiver/purefareceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ require (
github.com/miekg/dns v1.1.56 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
Loading