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.
This commit is contained in:
Brian Brazil 2017-03-27 16:18:33 +01:00 committed by Brian Brazil
parent d42e01b07c
commit 8cd5aff8fe
2 changed files with 58 additions and 33 deletions

View file

@ -74,6 +74,16 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
} }
sort.Sort(byName(vector)) 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 ( var (
lastMetricName model.LabelValue lastMetricName model.LabelValue
protMetricFam *dto.MetricFamily protMetricFam *dto.MetricFamily
@ -86,14 +96,13 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
} }
// Sort labelnames for unittest consistency. // 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 { for ln := range s.Metric {
labelnames = append(labelnames, string(ln)) labelNames = append(labelNames, ln)
} }
sort.Strings(labelnames) sort.Sort(labelNames)
for _, labelname := range labelnames { for _, ln := range labelNames {
ln := model.LabelName(labelname)
lv := s.Metric[ln] lv := s.Metric[ln]
if lv == "" { if lv == "" {
// No value means unset. Never consider those labels. // 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)), Name: proto.String(string(ln)),
Value: proto.String(string(lv)), Value: proto.String(string(lv)),
}) })
if _, ok := h.externalLabels[ln]; ok { if _, ok := externalLabels[ln]; ok {
globalUsed[ln] = struct{}{} globalUsed[ln] = struct{}{}
} }
} }
@ -136,7 +145,8 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
continue continue
} }
// Attach global labels if they do not exist yet. // 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 { if _, ok := globalUsed[ln]; !ok {
protMetric.Label = append(protMetric.Label, &dto.LabelPair{ protMetric.Label = append(protMetric.Label, &dto.LabelPair{
Name: proto.String(string(ln)), Name: proto.String(string(ln)),

View file

@ -59,72 +59,72 @@ var scenarios = map[string]struct {
params: "match[]=test_metric1", params: "match[]=test_metric1",
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="bar"} 10000 6000000 test_metric1{foo="bar",instance="i"} 10000 6000000
test_metric1{foo="boo"} 1 6000000 test_metric1{foo="boo",instance="i"} 1 6000000
`, `,
}, },
"test_metric2": { "test_metric2": {
params: "match[]=test_metric2", params: "match[]=test_metric2",
code: 200, code: 200,
body: `# TYPE test_metric2 untyped body: `# TYPE test_metric2 untyped
test_metric2{foo="boo"} 1 6000000 test_metric2{foo="boo",instance="i"} 1 6000000
`, `,
}, },
"test_metric_without_labels": { "test_metric_without_labels": {
params: "match[]=test_metric_without_labels", params: "match[]=test_metric_without_labels",
code: 200, code: 200,
body: `# TYPE test_metric_without_labels untyped body: `# TYPE test_metric_without_labels untyped
test_metric_without_labels 1001 6000000 test_metric_without_labels{instance=""} 1001 6000000
`, `,
}, },
"{foo='boo'}": { "{foo='boo'}": {
params: "match[]={foo='boo'}", params: "match[]={foo='boo'}",
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="boo"} 1 6000000 test_metric1{foo="boo",instance="i"} 1 6000000
# TYPE test_metric2 untyped # TYPE test_metric2 untyped
test_metric2{foo="boo"} 1 6000000 test_metric2{foo="boo",instance="i"} 1 6000000
`, `,
}, },
"two matchers": { "two matchers": {
params: "match[]=test_metric1&match[]=test_metric2", params: "match[]=test_metric1&match[]=test_metric2",
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="bar"} 10000 6000000 test_metric1{foo="bar",instance="i"} 10000 6000000
test_metric1{foo="boo"} 1 6000000 test_metric1{foo="boo",instance="i"} 1 6000000
# TYPE test_metric2 untyped # TYPE test_metric2 untyped
test_metric2{foo="boo"} 1 6000000 test_metric2{foo="boo",instance="i"} 1 6000000
`, `,
}, },
"everything": { "everything": {
params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'. params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'.
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="bar"} 10000 6000000 test_metric1{foo="bar",instance="i"} 10000 6000000
test_metric1{foo="boo"} 1 6000000 test_metric1{foo="boo",instance="i"} 1 6000000
# TYPE test_metric2 untyped # TYPE test_metric2 untyped
test_metric2{foo="boo"} 1 6000000 test_metric2{foo="boo",instance="i"} 1 6000000
# TYPE test_metric_without_labels untyped # 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": { "empty label value matches everything that doesn't have that label": {
params: "match[]={foo='',__name__=~'.%2b'}", params: "match[]={foo='',__name__=~'.%2b'}",
code: 200, code: 200,
body: `# TYPE test_metric_without_labels untyped 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": { "empty label value for a label that doesn't exist at all, matches everything": {
params: "match[]={bar='',__name__=~'.%2b'}", params: "match[]={bar='',__name__=~'.%2b'}",
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="bar"} 10000 6000000 test_metric1{foo="bar",instance="i"} 10000 6000000
test_metric1{foo="boo"} 1 6000000 test_metric1{foo="boo",instance="i"} 1 6000000
# TYPE test_metric2 untyped # TYPE test_metric2 untyped
test_metric2{foo="boo"} 1 6000000 test_metric2{foo="boo",instance="i"} 1 6000000
# TYPE test_metric_without_labels untyped # 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": { "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"}, externalLabels: model.LabelSet{"zone": "ie", "foo": "baz"},
code: 200, code: 200,
body: `# TYPE test_metric1 untyped body: `# TYPE test_metric1 untyped
test_metric1{foo="bar",zone="ie"} 10000 6000000 test_metric1{foo="bar",instance="i",zone="ie"} 10000 6000000
test_metric1{foo="boo",zone="ie"} 1 6000000 test_metric1{foo="boo",instance="i",zone="ie"} 1 6000000
# TYPE test_metric2 untyped # 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 # 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) { func TestFederation(t *testing.T) {
suite, err := promql.NewTest(t, ` suite, err := promql.NewTest(t, `
load 1m load 1m
test_metric1{foo="bar"} 0+100x100 test_metric1{foo="bar",instance="i"} 0+100x100
test_metric1{foo="boo"} 1+0x100 test_metric1{foo="boo",instance="i"} 1+0x100
test_metric2{foo="boo"} 1+0x100 test_metric2{foo="boo",instance="i"} 1+0x100
test_metric_without_labels 1+10x100 test_metric_without_labels 1+10x100
`) `)
if err != nil { if err != nil {
@ -189,7 +204,7 @@ func TestFederation(t *testing.T) {
t.Errorf("Scenario %q: got code %d, want %d", name, got, want) t.Errorf("Scenario %q: got code %d, want %d", name, got, want)
} }
if got, want := normalizeBody(res.Body), scenario.body; 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)
} }
} }
} }