mirror of https://github.com/prometheus/prometheus
Send instance="" with federation if instance not set.
This is needed for federating non-instance level metrics, so they don't end up with the instance label of the prometheus target. Also sort external labels, so label output order is consistent.pull/2549/head
parent
d42e01b07c
commit
8cd5aff8fe
|
@ -74,6 +74,16 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
sort.Sort(byName(vector))
|
||||
|
||||
externalLabels := h.externalLabels.Clone()
|
||||
if _, ok := externalLabels[model.InstanceLabel]; !ok {
|
||||
externalLabels[model.InstanceLabel] = ""
|
||||
}
|
||||
externalLabelNames := make(model.LabelNames, 0, len(externalLabels))
|
||||
for ln := range externalLabels {
|
||||
externalLabelNames = append(externalLabelNames, ln)
|
||||
}
|
||||
sort.Sort(externalLabelNames)
|
||||
|
||||
var (
|
||||
lastMetricName model.LabelValue
|
||||
protMetricFam *dto.MetricFamily
|
||||
|
@ -86,14 +96,13 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
// Sort labelnames for unittest consistency.
|
||||
labelnames := make([]string, 0, len(s.Metric))
|
||||
labelNames := make(model.LabelNames, 0, len(s.Metric))
|
||||
for ln := range s.Metric {
|
||||
labelnames = append(labelnames, string(ln))
|
||||
labelNames = append(labelNames, ln)
|
||||
}
|
||||
sort.Strings(labelnames)
|
||||
sort.Sort(labelNames)
|
||||
|
||||
for _, labelname := range labelnames {
|
||||
ln := model.LabelName(labelname)
|
||||
for _, ln := range labelNames {
|
||||
lv := s.Metric[ln]
|
||||
if lv == "" {
|
||||
// No value means unset. Never consider those labels.
|
||||
|
@ -127,7 +136,7 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
Name: proto.String(string(ln)),
|
||||
Value: proto.String(string(lv)),
|
||||
})
|
||||
if _, ok := h.externalLabels[ln]; ok {
|
||||
if _, ok := externalLabels[ln]; ok {
|
||||
globalUsed[ln] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +145,8 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
continue
|
||||
}
|
||||
// Attach global labels if they do not exist yet.
|
||||
for ln, lv := range h.externalLabels {
|
||||
for _, ln := range externalLabelNames {
|
||||
lv := externalLabels[ln]
|
||||
if _, ok := globalUsed[ln]; !ok {
|
||||
protMetric.Label = append(protMetric.Label, &dto.LabelPair{
|
||||
Name: proto.String(string(ln)),
|
||||
|
|
|
@ -59,72 +59,72 @@ var scenarios = map[string]struct {
|
|||
params: "match[]=test_metric1",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar"} 10000 6000000
|
||||
test_metric1{foo="boo"} 1 6000000
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
`,
|
||||
},
|
||||
"test_metric2": {
|
||||
params: "match[]=test_metric2",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
`,
|
||||
},
|
||||
"test_metric_without_labels": {
|
||||
params: "match[]=test_metric_without_labels",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels 1001 6000000
|
||||
test_metric_without_labels{instance=""} 1001 6000000
|
||||
`,
|
||||
},
|
||||
"{foo='boo'}": {
|
||||
params: "match[]={foo='boo'}",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="boo"} 1 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
`,
|
||||
},
|
||||
"two matchers": {
|
||||
params: "match[]=test_metric1&match[]=test_metric2",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar"} 10000 6000000
|
||||
test_metric1{foo="boo"} 1 6000000
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
`,
|
||||
},
|
||||
"everything": {
|
||||
params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'.
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar"} 10000 6000000
|
||||
test_metric1{foo="boo"} 1 6000000
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels 1001 6000000
|
||||
test_metric_without_labels{instance=""} 1001 6000000
|
||||
`,
|
||||
},
|
||||
"empty label value matches everything that doesn't have that label": {
|
||||
params: "match[]={foo='',__name__=~'.%2b'}",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels 1001 6000000
|
||||
test_metric_without_labels{instance=""} 1001 6000000
|
||||
`,
|
||||
},
|
||||
"empty label value for a label that doesn't exist at all, matches everything": {
|
||||
params: "match[]={bar='',__name__=~'.%2b'}",
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar"} 10000 6000000
|
||||
test_metric1{foo="boo"} 1 6000000
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels 1001 6000000
|
||||
test_metric_without_labels{instance=""} 1001 6000000
|
||||
`,
|
||||
},
|
||||
"external labels are added if not already present": {
|
||||
|
@ -132,12 +132,27 @@ test_metric_without_labels 1001 6000000
|
|||
externalLabels: model.LabelSet{"zone": "ie", "foo": "baz"},
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar",zone="ie"} 10000 6000000
|
||||
test_metric1{foo="boo",zone="ie"} 1 6000000
|
||||
test_metric1{foo="bar",instance="i",zone="ie"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i",zone="ie"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo",zone="ie"} 1 6000000
|
||||
test_metric2{foo="boo",instance="i",zone="ie"} 1 6000000
|
||||
# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels{zone="ie",foo="baz"} 1001 6000000
|
||||
test_metric_without_labels{foo="baz",instance="",zone="ie"} 1001 6000000
|
||||
`,
|
||||
},
|
||||
"instance is an external label": {
|
||||
// This makes no sense as a configuration, but we should
|
||||
// know what it does anyway.
|
||||
params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'.
|
||||
externalLabels: model.LabelSet{"instance": "baz"},
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
test_metric1{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric2 untyped
|
||||
test_metric2{foo="boo",instance="i"} 1 6000000
|
||||
# TYPE test_metric_without_labels untyped
|
||||
test_metric_without_labels{instance="baz"} 1001 6000000
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
@ -145,9 +160,9 @@ test_metric_without_labels{zone="ie",foo="baz"} 1001 6000000
|
|||
func TestFederation(t *testing.T) {
|
||||
suite, err := promql.NewTest(t, `
|
||||
load 1m
|
||||
test_metric1{foo="bar"} 0+100x100
|
||||
test_metric1{foo="boo"} 1+0x100
|
||||
test_metric2{foo="boo"} 1+0x100
|
||||
test_metric1{foo="bar",instance="i"} 0+100x100
|
||||
test_metric1{foo="boo",instance="i"} 1+0x100
|
||||
test_metric2{foo="boo",instance="i"} 1+0x100
|
||||
test_metric_without_labels 1+10x100
|
||||
`)
|
||||
if err != nil {
|
||||
|
@ -189,7 +204,7 @@ func TestFederation(t *testing.T) {
|
|||
t.Errorf("Scenario %q: got code %d, want %d", name, got, want)
|
||||
}
|
||||
if got, want := normalizeBody(res.Body), scenario.body; got != want {
|
||||
t.Errorf("Scenario %q: got body %q, want %q", name, got, want)
|
||||
t.Errorf("Scenario %q: got body %s, want %s", name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue