Skip to content

Commit

Permalink
[chore] [reveiver/prometheusreceiver] Add unit tests around protobuf …
Browse files Browse the repository at this point in the history
…negotiation (#29153)

The code needs some basic tests that can be later expanded with tests
for native histograms use cases.

Changes:
Refactored `testComponent` function to be easier to customize the
configuration of the scrape.
Expanded `compareHistogram` to assert on the explicit boundaries as
well.
Added function `prometheusMetricFamilyToProtoBuf` to helpers to be able
to turn serialize a Prometheus metric family
into Protobuf.
Added simple test of Protobuf based scrape of counters, gauges,
summaries and histograms.

#26555  

Followup to #27030 
Related to #28663 

**Testing:**

Adding simple e2e test for scraping over Protobuf. 

**Documentation:** 

Not applicable.

---------

Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
Co-authored-by: David Ashpole <dashpole@google.com>
  • Loading branch information
krajorama and dashpole committed Nov 15, 2023
1 parent d6b1471 commit bc25618
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 68 deletions.
57 changes: 47 additions & 10 deletions receiver/prometheusreceiver/metrics_receiver_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package prometheusreceiver

import (
"bytes"
"context"
"encoding/binary"
"fmt"
"log"
"math"
Expand All @@ -17,9 +19,11 @@ import (
"time"

gokitlog "github.com/go-kit/log"
"github.com/gogo/protobuf/proto"
promcfg "github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/value"
dto "github.com/prometheus/prometheus/prompb/io/prometheus/client"
"github.com/prometheus/prometheus/scrape"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -37,6 +41,9 @@ type mockPrometheusResponse struct {
code int
data string
useOpenMetrics bool

useProtoBuf bool // This overrides data and useOpenMetrics above
buf []byte
}

type mockPrometheus struct {
Expand Down Expand Up @@ -82,11 +89,18 @@ func (mp *mockPrometheus) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(404)
return
}
if pages[index].useOpenMetrics {
switch {
case pages[index].useProtoBuf:
rw.Header().Set("Content-Type", "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited")
case pages[index].useOpenMetrics:
rw.Header().Set("Content-Type", "application/openmetrics-text")
}
rw.WriteHeader(pages[index].code)
_, _ = rw.Write([]byte(pages[index].data))
if pages[index].useProtoBuf {
_, _ = rw.Write(pages[index].buf)
} else {
_, _ = rw.Write([]byte(pages[index].data))
}
}

func (mp *mockPrometheus) Close() {
Expand Down Expand Up @@ -563,10 +577,11 @@ func assertNormalNan() numberPointComparator {
}
}

func compareHistogram(count uint64, sum float64, buckets []uint64) histogramPointComparator {
func compareHistogram(count uint64, sum float64, upperBounds []float64, buckets []uint64) histogramPointComparator {
return func(t *testing.T, histogramDataPoint pmetric.HistogramDataPoint) {
assert.Equal(t, count, histogramDataPoint.Count(), "Histogram count value does not match")
assert.Equal(t, sum, histogramDataPoint.Sum(), "Histogram sum value does not match")
assert.Equal(t, upperBounds, histogramDataPoint.ExplicitBounds().AsRaw(), "Histogram upper bounds values do not match")
assert.Equal(t, buckets, histogramDataPoint.BucketCounts().AsRaw(), "Histogram bucket count values do not match")
}
}
Expand All @@ -593,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, useStartTimeMetric bool, trimMetricSuffixes bool, startTimeMetricRegex string, cfgMuts ...func(*promcfg.Config)) {
func testComponent(t *testing.T, targets []*testData, alterConfig func(*Config), cfgMuts ...func(*promcfg.Config)) {
ctx := context.Background()
mp, cfg, err := setupMockPrometheus(targets...)
for _, cfgMut := range cfgMuts {
Expand All @@ -602,13 +617,16 @@ func testComponent(t *testing.T, targets []*testData, useStartTimeMetric bool, t
require.Nilf(t, err, "Failed to create Prometheus config: %v", err)
defer mp.Close()

cms := new(consumertest.MetricsSink)
receiver := newPrometheusReceiver(receivertest.NewNopCreateSettings(), &Config{
config := &Config{
PrometheusConfig: cfg,
UseStartTimeMetric: useStartTimeMetric,
StartTimeMetricRegex: startTimeMetricRegex,
TrimMetricSuffixes: trimMetricSuffixes,
}, cms)
StartTimeMetricRegex: "",
}
if alterConfig != nil {
alterConfig(config)
}

cms := new(consumertest.MetricsSink)
receiver := newPrometheusReceiver(receivertest.NewNopCreateSettings(), config, cms)

require.NoError(t, receiver.Start(ctx, componenttest.NewNopHost()))
// verify state after shutdown is called
Expand Down Expand Up @@ -695,3 +713,22 @@ func getTS(ms pmetric.MetricSlice) pcommon.Timestamp {
}
return 0
}

func prometheusMetricFamilyToProtoBuf(t *testing.T, buffer *bytes.Buffer, metricFamily *dto.MetricFamily) *bytes.Buffer {
if buffer == nil {
buffer = &bytes.Buffer{}
}

data, err := proto.Marshal(metricFamily)
require.NoError(t, err)

varintBuf := make([]byte, binary.MaxVarintLen32)
varintLength := binary.PutUvarint(varintBuf, uint64(len(data)))

_, err = buffer.Write(varintBuf[:varintLength])
require.NoError(t, err)
_, err = buffer.Write(data)
require.NoError(t, err)

return buffer
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func TestHonorTimeStampsWithTrue(t *testing.T) {
},
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

// TestHonorTimeStampsWithFalse validates that with honor_timestamp config set to false,
Expand All @@ -168,7 +168,7 @@ func TestHonorTimeStampsWithFalse(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
for _, scrapeConfig := range cfg.ScrapeConfigs {
scrapeConfig.HonorTimestamps = false
}
Expand Down Expand Up @@ -246,7 +246,7 @@ func verifyHonorTimeStampsTrue(t *testing.T, td *testData, resourceMetrics []pme
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts4))),
compareHistogramTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts4))),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.05, 0.5, 1}, []uint64{1000, 500, 500, 500}),
},
},
}),
Expand Down Expand Up @@ -310,7 +310,7 @@ func verifyHonorTimeStampsTrue(t *testing.T, td *testData, resourceMetrics []pme
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts9))),
compareHistogramTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts9))),
compareHistogram(2400, 4950, []uint64{900, 500, 500, 500}),
compareHistogram(2400, 4950, []float64{0.05, 0.5, 1}, []uint64{900, 500, 500, 500}),
},
},
}),
Expand Down Expand Up @@ -374,7 +374,7 @@ func verifyHonorTimeStampsTrue(t *testing.T, td *testData, resourceMetrics []pme
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts9))),
compareHistogramTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(ts14))),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.05, 0.5, 1}, []uint64{1000, 500, 500, 500}),
},
},
}),
Expand Down Expand Up @@ -446,7 +446,7 @@ func verifyHonorTimeStampsFalse(t *testing.T, td *testData, resourceMetrics []pm
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(ts1),
compareHistogramTimestamp(ts1),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.05, 0.5, 1}, []uint64{1000, 500, 500, 500}),
},
},
}),
Expand Down Expand Up @@ -512,7 +512,7 @@ func verifyHonorTimeStampsFalse(t *testing.T, td *testData, resourceMetrics []pm
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(ts2),
compareHistogramTimestamp(ts2),
compareHistogram(2400, 4950, []uint64{900, 500, 500, 500}),
compareHistogram(2400, 4950, []float64{0.05, 0.5, 1}, []uint64{900, 500, 500, 500}),
},
},
}),
Expand Down
26 changes: 13 additions & 13 deletions receiver/prometheusreceiver/metrics_receiver_labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestExternalLabels(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
cfg.GlobalConfig.ExternalLabels = labels.FromStrings("key", "value")
})
}
Expand Down Expand Up @@ -121,7 +121,7 @@ func TestLabelLimitConfig(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
// set label limit in scrape_config
for _, scrapeCfg := range cfg.ScrapeConfigs {
scrapeCfg.LabelLimit = 5
Expand Down Expand Up @@ -198,7 +198,7 @@ func verifyLabelConfigTarget1(t *testing.T, td *testData, rms []pmetric.Resource
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(ts1),
compareHistogramTimestamp(ts1),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.1, 0.5, 1}, []uint64{1000, 500, 500, 500}),
compareHistogramAttributes(map[string]string{"label1": "value1", "label2": "value2"}),
},
},
Expand Down Expand Up @@ -248,7 +248,7 @@ func TestLabelNameLimitConfig(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
// set label limit in scrape_config
for _, scrapeCfg := range cfg.ScrapeConfigs {
scrapeCfg.LabelNameLengthLimit = 20
Expand Down Expand Up @@ -284,7 +284,7 @@ func TestLabelValueLimitConfig(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
// set label name limit in scrape_config
for _, scrapeCfg := range cfg.ScrapeConfigs {
scrapeCfg.LabelValueLengthLimit = 25
Expand Down Expand Up @@ -360,7 +360,7 @@ func verifyEmptyLabelValuesTarget1(t *testing.T, td *testData, rms []pmetric.Res
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(ts1),
compareHistogramTimestamp(ts1),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.1, 0.5, 1}, []uint64{1000, 500, 500, 500}),
compareHistogramAttributes(map[string]string{"id": "1"}),
},
},
Expand Down Expand Up @@ -462,7 +462,7 @@ func TestEmptyLabelValues(t *testing.T) {
validateFunc: verifyEmptyLabelValuesTarget2,
},
}
testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

const honorLabelsTarget = `
Expand Down Expand Up @@ -556,7 +556,7 @@ func TestEmptyLabels(t *testing.T) {
validateFunc: verifyEmptyLabelsTarget1,
},
}
testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func TestHonorLabelsFalseConfig(t *testing.T) {
Expand All @@ -570,7 +570,7 @@ func TestHonorLabelsFalseConfig(t *testing.T) {
},
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyHonorLabelsTrue(t *testing.T, td *testData, rms []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -613,7 +613,7 @@ func TestHonorLabelsTrueConfig(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
// set label name limit in scrape_config
for _, scrapeCfg := range cfg.ScrapeConfigs {
scrapeCfg.HonorLabels = true
Expand All @@ -639,7 +639,7 @@ func TestRelabelJobInstance(t *testing.T) {
},
}

testComponent(t, targets, false, false, "", func(cfg *promcfg.Config) {
testComponent(t, targets, nil, func(cfg *promcfg.Config) {
for _, scrapeConfig := range cfg.ScrapeConfigs {
scrapeConfig.MetricRelabelConfigs = []*relabel.Config{
{
Expand Down Expand Up @@ -709,7 +709,7 @@ func TestTargetInfoResourceAttributes(t *testing.T) {
},
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyTargetInfoResourceAttributes(t *testing.T, td *testData, rms []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -759,7 +759,7 @@ func TestScopeInfoScopeAttributes(t *testing.T) {
},
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyMultipleScopes(t *testing.T, td *testData, rms []pmetric.ResourceMetrics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ func TestMetricNormalize(t *testing.T) {
},
}

testComponent(t, targets, false, true, "")
testComponent(t, targets, func(c *Config) {
c.TrimMetricSuffixes = true
})
}

func verifyNormalizeMetric(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestStaleNaNs(t *testing.T) {
validateScrapes: true,
},
}
testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyStaleNaNs(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -133,7 +133,7 @@ func verifyStaleNaNsSuccessfulScrape(t *testing.T, td *testData, resourceMetric
histogramPointComparator: []histogramPointComparator{
compareHistogramStartTimestamp(startTimestamp),
compareHistogramTimestamp(ts1),
compareHistogram(2500, 5000, []uint64{1000, 500, 500, 500}),
compareHistogram(2500, 5000, []float64{0.05, 0.5, 1}, []uint64{1000, 500, 500, 500}),
},
},
}),
Expand Down Expand Up @@ -252,7 +252,7 @@ func TestNormalNaNs(t *testing.T) {
validateFunc: verifyNormalNaNs,
},
}
testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyNormalNaNs(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -339,7 +339,7 @@ func TestInfValues(t *testing.T) {
validateFunc: verifyInfValues,
},
}
testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyInfValues(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestOpenMetricsPositive(t *testing.T) {
targets = append(targets, testData)
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyFailTarget(t *testing.T, td *testData, mds []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestOpenMetricsFail(t *testing.T) {
targets = append(targets, testData)
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

func verifyInvalidTarget(t *testing.T, td *testData, mds []pmetric.ResourceMetrics) {
Expand Down Expand Up @@ -142,7 +142,7 @@ func TestOpenMetricsInvalid(t *testing.T) {
targets = append(targets, testData)
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)
}

// reads test data from testdata/openmetrics directory
Expand Down Expand Up @@ -228,7 +228,7 @@ func TestInfoStatesetMetrics(t *testing.T) {
},
}

testComponent(t, targets, false, false, "")
testComponent(t, targets, nil)

}

Expand Down
Loading

0 comments on commit bc25618

Please sign in to comment.