Skip to content

Commit

Permalink
Remove servergroup added labels from matcher selectors for LabelValue…
Browse files Browse the repository at this point in the history
…s to downstream

If a label was only added by promxy *and* a matcher we'd still pass that
matcher downstream. This is a problem as promxy is the one who added it
-- so it doesn't matter if downstream knows about it (usually it
doesn't).

Fixes #665
  • Loading branch information
jacksontj committed Sep 10, 2024
1 parent 7c5eda6 commit 20ab35d
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
36 changes: 36 additions & 0 deletions pkg/promclient/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ func (c *AddLabelClient) Key() model.LabelSet {
return c.Labels
}

func (c *AddLabelClient) filterMatchers(matchers []string) ([]string, bool, error) {
ret := make([]string, 0, len(matchers))
for i, matcher := range matchers {
selectors, err := parser.ParseMetricSelector(matcher)
if err != nil {
return nil, true, err
}

// If the selector matches our value -- remove the selector
// if the selector doesn't match, return empty
for sI, s := range selectors {
if v, ok := c.Labels[model.LabelName(s.Name)]; ok {
if s.Matches(string(v)) {
selectors = append(selectors[:sI], selectors[i+1:]...)
} else {
return nil, false, nil
}
}
}
newMatcher, err := promhttputil.MatcherToString(selectors)
if err != nil {
return nil, false, err
}
ret = append(ret, newMatcher)
}
return ret, true, nil
}

// LabelNames returns all the unique label names present in the block in sorted order.
func (c *AddLabelClient) LabelNames(ctx context.Context, matchers []string, startTime time.Time, endTime time.Time) ([]string, v1.Warnings, error) {
l, w, err := c.API.LabelNames(ctx, matchers, startTime, endTime)
Expand All @@ -81,6 +109,14 @@ func (c *AddLabelClient) LabelNames(ctx context.Context, matchers []string, star

// LabelValues performs a query for the values of the given label.
func (c *AddLabelClient) LabelValues(ctx context.Context, label string, matchers []string, startTime time.Time, endTime time.Time) (model.LabelValues, v1.Warnings, error) {
matchers, ok, err := c.filterMatchers(matchers)
if err != nil {
return nil, nil, err
}
if !ok {
return nil, nil, nil
}

val, w, err := c.API.LabelValues(ctx, label, matchers, startTime, endTime)
if err != nil {
return nil, w, err
Expand Down
125 changes: 125 additions & 0 deletions pkg/promclient/label_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package promclient

import (
"context"
"fmt"
"reflect"
"strconv"
"strings"
"testing"
"time"

v1 "github.com/prometheus/client_golang/api/prometheus/v1"
model "github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql/parser"
)

func TestMergeLabelValues(t *testing.T) {
Expand Down Expand Up @@ -85,3 +92,121 @@ func TestMergeLabelSets(t *testing.T) {
})
}
}

type labelStubAPI struct {
series []model.LabelSet
}

func (a *labelStubAPI) LabelNames(ctx context.Context, matchers []string, startTime time.Time, endTime time.Time) ([]string, v1.Warnings, error) {
return nil, nil, fmt.Errorf("not implemented")
}

func (a *labelStubAPI) LabelValues(ctx context.Context, label string, matchers []string, startTime time.Time, endTime time.Time) (model.LabelValues, v1.Warnings, error) {
series := append([]model.LabelSet{}, a.series...)
for _, m := range matchers {
selectors, err := parser.ParseMetricSelector(m)
if err != nil {
return nil, nil, err
}
for _, selector := range selectors {
for i, s := range series {
v, ok := s[model.LabelName(selector.Name)]
if !ok || !selector.Matches(string(v)) {
series = append(series[:i], series[i+1:]...)
}
}
}
}
values := make(map[string]struct{})
for _, s := range series {
for k, v := range s {
if strings.HasPrefix(string(k), model.ReservedLabelPrefix) {
continue
}
values[string(v)] = struct{}{}
}
}
ret := make(model.LabelValues, 0, len(values))
for k := range values {
ret = append(ret, model.LabelValue(k))
}
return ret, nil, nil
}

// Query performs a query for the given time.
func (a *labelStubAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, v1.Warnings, error) {
return nil, nil, fmt.Errorf("not implemented")

}

// QueryRange performs a query for the given range.
func (a *labelStubAPI) QueryRange(ctx context.Context, query string, r v1.Range) (model.Value, v1.Warnings, error) {
return nil, nil, fmt.Errorf("not implemented")
}

// Series finds series by label matchers.
func (a *labelStubAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, v1.Warnings, error) {
return nil, nil, fmt.Errorf("not implemented")

}

// GetValue loads the raw data for a given set of matchers in the time range
func (a *labelStubAPI) GetValue(ctx context.Context, start, end time.Time, matchers []*labels.Matcher) (model.Value, v1.Warnings, error) {
return nil, nil, fmt.Errorf("not implemented")

}

// Metadata returns metadata about metrics currently scraped by the metric name.
func (a *labelStubAPI) Metadata(ctx context.Context, metric, limit string) (map[string][]v1.Metadata, error) {
return nil, fmt.Errorf("not implemented")
}
func TestAddLabelClient(t *testing.T) {

stub := &labelStubAPI{
series: []model.LabelSet{{model.MetricNameLabel: "testmetric", "a": "1"}},
}

tests := []struct {
labelSet model.LabelSet
err bool
matchers []string
labelValues []string
}{
{
labelSet: model.LabelSet{"b": "1"},
labelValues: []string{"1"},
},
{
labelSet: model.LabelSet{"b": "1"},
labelValues: []string{"1"},
matchers: []string{`{b="1"}`},
},
}

for i, test := range tests {
a := &AddLabelClient{stub, test.labelSet}
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Run("LabelNames", func(t *testing.T) {
v, _, err := a.LabelValues(context.TODO(), "a", test.matchers, time.Time{}, time.Time{})
if err != nil != test.err {
if test.err {
t.Fatalf("missing expected err")
} else {
t.Fatalf("Unexpected Err: %v", err)
}
}
if err == nil {
if len(v) != len(test.labelValues) {
t.Fatalf("mismatch in len: \nexpected=%v\nactual=%v", test.labelValues, v)
}

for i, actualV := range v {
if actualV != model.LabelValue(test.labelValues[i]) {
t.Fatalf("mismatch in value: \nexpected=%v\nactual=%v", test.labelValues, v)
}
}
}
})
})
}
}

0 comments on commit 20ab35d

Please sign in to comment.