Remote read endpoint should handle matchers for external labels. (#3325)

If the other Prometheus has an external label that matches that of
the Prometheus being read from, then we need to remove that matcher
from the request as it's not actually stored in the database - it's
only added for alerts, federation and on the output of the remote read
endpoint.
Instead we check for that label being empty, in case there is a time
series with a different label value for that external label.
pull/3405/head v1.8.1
Brian Brazil 7 years ago committed by GitHub
parent f6df3b7d57
commit 3a7c51ab70

@ -1,6 +1,6 @@
## 1.8.1 / 2017-10-19 ## 1.8.1 / 2017-10-19
* [BUGFIX] Apply external labels to remote read endpoint * [BUGFIX] Correctly handle external labels on remote read endpoint
## 1.8.0 / 2017-10-06 ## 1.8.0 / 2017-10-06

@ -486,7 +486,26 @@ func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
iters, err := querier.QueryRange(r.Context(), from, through, matchers...) // Change equality matchers which match external labels
// to a matcher that looks for an empty label,
// as that label should not be present in the storage.
externalLabels := api.config().GlobalConfig.ExternalLabels.Clone()
filteredMatchers := make([]*metric.LabelMatcher, 0, len(matchers))
for _, m := range matchers {
value := externalLabels[m.Name]
if m.Type == metric.Equal && value == m.Value {
matcher, err := metric.NewLabelMatcher(metric.Equal, m.Name, "")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
filteredMatchers = append(filteredMatchers, matcher)
} else {
filteredMatchers = append(filteredMatchers, m)
}
}
iters, err := querier.QueryRange(r.Context(), from, through, filteredMatchers...)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -496,7 +515,6 @@ func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) {
OldestInclusive: from, OldestInclusive: from,
NewestInclusive: through, NewestInclusive: through,
})) }))
externalLabels := api.config().GlobalConfig.ExternalLabels.Clone()
for _, ts := range resp.Results[i].Timeseries { for _, ts := range resp.Results[i].Timeseries {
globalUsed := map[string]struct{}{} globalUsed := map[string]struct{}{}
for _, l := range ts.Labels { for _, l := range ts.Labels {

@ -560,6 +560,7 @@ func TestReadEndpoint(t *testing.T) {
ExternalLabels: model.LabelSet{ ExternalLabels: model.LabelSet{
"baz": "a", "baz": "a",
"b": "c", "b": "c",
"d": "e",
}, },
}, },
} }
@ -567,11 +568,15 @@ func TestReadEndpoint(t *testing.T) {
} }
// Encode the request. // Encode the request.
matcher, err := metric.NewLabelMatcher(metric.Equal, "__name__", "test_metric1") matcher1, err := metric.NewLabelMatcher(metric.Equal, "__name__", "test_metric1")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
query, err := remote.ToQuery(0, 1, metric.LabelMatchers{matcher}) matcher2, err := metric.NewLabelMatcher(metric.Equal, "d", "e")
if err != nil {
t.Fatal(err)
}
query, err := remote.ToQuery(0, 1, metric.LabelMatchers{matcher1, matcher2})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -611,7 +616,7 @@ func TestReadEndpoint(t *testing.T) {
result := remote.FromQueryResult(resp.Results[0]) result := remote.FromQueryResult(resp.Results[0])
expected := &model.Matrix{ expected := &model.Matrix{
&model.SampleStream{ &model.SampleStream{
Metric: model.Metric{"__name__": "test_metric1", "b": "c", "baz": "qux", "foo": "bar"}, Metric: model.Metric{"__name__": "test_metric1", "b": "c", "d": "e", "baz": "qux", "foo": "bar"},
Values: []model.SamplePair{model.SamplePair{Value: 1, Timestamp: 0}}, Values: []model.SamplePair{model.SamplePair{Value: 1, Timestamp: 0}},
}, },
} }

Loading…
Cancel
Save