1// Copyright 2017 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package remote
15
16import (
17	"reflect"
18	"sort"
19	"testing"
20
21	"github.com/prometheus/common/model"
22	"github.com/prometheus/prometheus/storage/metric"
23)
24
25func TestValidateLabelsAndMetricName(t *testing.T) {
26	tests := []struct {
27		result      model.Matrix
28		expectedErr string
29		shouldPass  bool
30	}{
31		{
32			result: model.Matrix{
33				&model.SampleStream{
34					Metric: model.Metric{
35						"__name__":  "name",
36						"labelName": "labelValue",
37					},
38				},
39			},
40			expectedErr: "",
41			shouldPass:  true,
42		},
43		{
44			result: model.Matrix{
45				&model.SampleStream{
46					Metric: model.Metric{
47						"__name__":   "name",
48						"_labelName": "labelValue",
49					},
50				},
51			},
52			expectedErr: "",
53			shouldPass:  true,
54		},
55		{
56			result: model.Matrix{
57				&model.SampleStream{
58					Metric: model.Metric{
59						"__name__":   "name",
60						"@labelName": "labelValue",
61					},
62				},
63			},
64			expectedErr: "Invalid label name: @labelName",
65			shouldPass:  false,
66		},
67		{
68			result: model.Matrix{
69				&model.SampleStream{
70					Metric: model.Metric{
71						"__name__":     "name",
72						"123labelName": "labelValue",
73					},
74				},
75			},
76			expectedErr: "Invalid label name: 123labelName",
77			shouldPass:  false,
78		},
79		{
80			result: model.Matrix{
81				&model.SampleStream{
82					Metric: model.Metric{
83						"__name__": "name",
84						"":         "labelValue",
85					},
86				},
87			},
88			expectedErr: "Invalid label name: ",
89			shouldPass:  false,
90		},
91		{
92			result: model.Matrix{
93				&model.SampleStream{
94					Metric: model.Metric{
95						"__name__":  "name",
96						"labelName": model.LabelValue([]byte{0xff}),
97					},
98				},
99			},
100			expectedErr: "Invalid label value: " + string([]byte{0xff}),
101			shouldPass:  false,
102		},
103		{
104			result: model.Matrix{
105				&model.SampleStream{
106					Metric: model.Metric{
107						"__name__": "@invalid_name",
108					},
109				},
110			},
111			expectedErr: "Invalid metric name: @invalid_name",
112			shouldPass:  false,
113		},
114	}
115
116	for _, test := range tests {
117		err := validateLabelsAndMetricName(test.result)
118		if test.shouldPass {
119			if err != nil {
120				t.Fatalf("Test should pass, got unexpected error: %v", err)
121			}
122			continue
123		}
124		if err != nil {
125			if err.Error() != test.expectedErr {
126				t.Fatalf("Unexpected error, got: %v, expected: %v", err, test.expectedErr)
127			}
128		} else {
129			t.Fatalf("Expected error, got none")
130		}
131	}
132}
133
134func mustNewLabelMatcher(mt metric.MatchType, name model.LabelName, val model.LabelValue) *metric.LabelMatcher {
135	m, err := metric.NewLabelMatcher(mt, name, val)
136	if err != nil {
137		panic(err)
138	}
139	return m
140}
141
142func TestAddExternalLabels(t *testing.T) {
143	tests := []struct {
144		el          model.LabelSet
145		inMatchers  metric.LabelMatchers
146		outMatchers metric.LabelMatchers
147		added       model.LabelSet
148	}{
149		{
150			el: model.LabelSet{},
151			inMatchers: metric.LabelMatchers{
152				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
153			},
154			outMatchers: metric.LabelMatchers{
155				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
156			},
157			added: model.LabelSet{},
158		},
159		{
160			el: model.LabelSet{"region": "europe", "dc": "berlin-01"},
161			inMatchers: metric.LabelMatchers{
162				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
163			},
164			outMatchers: metric.LabelMatchers{
165				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
166				mustNewLabelMatcher(metric.Equal, "region", "europe"),
167				mustNewLabelMatcher(metric.Equal, "dc", "berlin-01"),
168			},
169			added: model.LabelSet{"region": "europe", "dc": "berlin-01"},
170		},
171		{
172			el: model.LabelSet{"region": "europe", "dc": "berlin-01"},
173			inMatchers: metric.LabelMatchers{
174				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
175				mustNewLabelMatcher(metric.Equal, "dc", "munich-02"),
176			},
177			outMatchers: metric.LabelMatchers{
178				mustNewLabelMatcher(metric.Equal, "job", "api-server"),
179				mustNewLabelMatcher(metric.Equal, "region", "europe"),
180				mustNewLabelMatcher(metric.Equal, "dc", "munich-02"),
181			},
182			added: model.LabelSet{"region": "europe"},
183		},
184	}
185
186	for i, test := range tests {
187		q := querier{
188			externalLabels: test.el,
189		}
190
191		matchers, added := q.addExternalLabels(test.inMatchers)
192
193		sort.Slice(test.outMatchers, func(i, j int) bool { return test.outMatchers[i].Name < test.outMatchers[j].Name })
194		sort.Slice(matchers, func(i, j int) bool { return matchers[i].Name < matchers[j].Name })
195
196		if !reflect.DeepEqual(matchers, test.outMatchers) {
197			t.Fatalf("%d. unexpected matchers; want %v, got %v", i, test.outMatchers, matchers)
198		}
199		if !reflect.DeepEqual(added, test.added) {
200			t.Fatalf("%d. unexpected added labels; want %v, got %v", i, test.added, added)
201		}
202	}
203}
204