-
Notifications
You must be signed in to change notification settings - Fork 0
/
processor.go
284 lines (227 loc) · 7.63 KB
/
processor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// Package mutable handles mutable labels.
package mutable
import (
"context"
"errors"
"fmt"
"regexp"
"regexp/syntax"
"sort"
"github.com/prometheus/prometheus/model/labels"
)
var (
errUnsupportedOperation = errors.New("unsupported operation")
errNoResult = errors.New("no result")
)
// LabelProcessor can replace mutable labels by non mutable labels.
type LabelProcessor struct {
labelProvider LabelProvider
tenantLabelName string
}
func NewLabelProcessor(provider LabelProvider, tenantLabelName string) *LabelProcessor {
processor := LabelProcessor{
labelProvider: provider,
tenantLabelName: tenantLabelName,
}
return &processor
}
// ReplaceMutableLabels searches for mutable labels and replace them by non mutable labels.
// For example if we have a mutable label group "mygroup" which contains 'server1' and 'server2',
// the label matcher group="mygroup" becomes instance="server1|server2".
func (lp *LabelProcessor) ReplaceMutableLabels(
ctx context.Context,
matchers []*labels.Matcher,
) ([]*labels.Matcher, error) {
processedMatchers := make([]*labels.Matcher, 0, len(matchers))
tenant := lp.tenantFromMatchers(matchers)
// Mutable labels are disabled when no tenant is found.
if tenant == "" {
return matchers, nil
}
// Search for mutable labels and replace them by non mutable labels.
for _, matcher := range matchers {
isMutableLabel, err := lp.IsMutableLabel(ctx, tenant, matcher.Name)
if err != nil {
return nil, err
}
if !isMutableLabel {
processedMatchers = append(processedMatchers, matcher)
continue
}
var newMatcher *labels.Matcher
if matcher.Type == labels.MatchRegexp || matcher.Type == labels.MatchNotRegexp {
newMatcher, err = lp.processMutableLabelRegex(ctx, tenant, matcher)
} else {
newMatcher, err = lp.processMutableLabel(ctx, tenant, matcher)
}
if err != nil {
return nil, err
}
// We don't need to check for collisions between the new matchers and the existing ones,
// Prometheus accepts queries with multiple labels with the same name.
processedMatchers = append(processedMatchers, newMatcher)
}
return processedMatchers, nil
}
// IsMutableLabel returns whether the label name is mutable.
func (lp *LabelProcessor) IsMutableLabel(ctx context.Context, tenant, name string) (bool, error) {
mutableLabelNames, err := lp.labelProvider.MutableLabelNames(ctx, tenant)
if err != nil {
return false, err
}
for _, mutName := range mutableLabelNames {
if name == mutName {
return true, nil
}
}
return false, nil
}
func (lp *LabelProcessor) tenantFromMatchers(matchers []*labels.Matcher) string {
for _, matcher := range matchers {
if matcher.Name == lp.tenantLabelName {
return matcher.Value
}
}
return ""
}
// processMutableLabel replaces a mutable matcher by non mutable matchers.
func (lp *LabelProcessor) processMutableLabel(ctx context.Context,
tenant string,
matcher *labels.Matcher,
) (*labels.Matcher, error) {
lbls, err := lp.labelProvider.GetNonMutable(ctx, tenant, matcher.Name, matcher.Value)
if err != nil {
return nil, err
}
newMatcher, err := mergeLabels(lbls, matcher.Type)
if err != nil {
return nil, fmt.Errorf("merge labels: %w", err)
}
return newMatcher, nil
}
// processMutableLabelRegex replaces a regex mutable matcher by non mutable matchers.
func (lp *LabelProcessor) processMutableLabelRegex(
ctx context.Context,
tenant string,
matcher *labels.Matcher,
) (*labels.Matcher, error) {
// Search for all matching values.
var matchingLabels NonMutableLabels
values, err := lp.labelProvider.AllValues(ctx, tenant, matcher.Name)
if err != nil {
return nil, err
}
for _, value := range values {
if matcher.Matches(value) {
lbls, err := lp.labelProvider.GetNonMutable(ctx, tenant, matcher.Name, value)
if err != nil {
return nil, err
}
// We currently only support matching on the same non mutable label name.
if matchingLabels.Name != "" && matchingLabels.Name != lbls.Name {
errMsg := "%w: mutable label regex '%s' returned two different non mutable label names: %s and %s"
return nil, fmt.Errorf(errMsg, errUnsupportedOperation, matcher.Name, matchingLabels.Name, lbls.Name)
}
matchingLabels = NonMutableLabels{
Name: lbls.Name,
Values: append(matchingLabels.Values, lbls.Values...),
}
}
}
if len(matchingLabels.Values) == 0 {
return nil, fmt.Errorf("%w: tenant=%s, matcher=%#v", errNoResult, tenant, matcher)
}
// The returned matcher is always a MatchRegexp, even if the matcher was a MatchNotRegexp,
// because we searched for matching values.
newMatcher, err := mergeLabels(matchingLabels, labels.MatchRegexp)
if err != nil {
return nil, fmt.Errorf("merge labels: %w", err)
}
return newMatcher, nil
}
// mergeLabels merge the labels in one matcher that matches any of the input label values.
// Example: mergeLabels(instance="server1", instance="server2") -> instance~="server1|server2".
func mergeLabels(lbls NonMutableLabels, matchType labels.MatchType) (matcher *labels.Matcher, err error) {
// Sort the values to have a deterministic output for testing.
sort.Strings(lbls.Values)
regex, err := MergeRegex(lbls.Values)
if err != nil {
return nil, err
}
newMatcher, err := labels.NewMatcher(regexMatchType(matchType), lbls.Name, regex)
if err != nil {
return nil, err
}
return newMatcher, nil
}
// MergeRegex returns a regular expression matching any of the input strings.
func MergeRegex(input []string) (string, error) {
var err error
allRegex := make([]*syntax.Regexp, len(input))
for i, v := range input {
escaped := regexp.QuoteMeta(v)
allRegex[i], err = syntax.Parse(escaped, syntax.Perl)
if err != nil {
return "", err
}
}
re := syntax.Regexp{
Op: syntax.OpAlternate,
Flags: syntax.Perl,
Sub: allRegex,
}
return re.String(), nil
}
// regexMatchType returns the regex match type corresponding to the given type.
func regexMatchType(matchType labels.MatchType) labels.MatchType {
if matchType == labels.MatchEqual || matchType == labels.MatchRegexp {
return labels.MatchRegexp
}
return labels.MatchNotRegexp
}
// AddMutableLabels searches for non mutable labels and add their corresponding mutable labels
// if they exist. For example if we have a mutable label group "mygroup" which contains 'server1',
// and the label instance="server1" as input, the label group="mygroup" will be added.
// It also removes any mutable labels present in the input.
func (lp *LabelProcessor) AddMutableLabels(ctx context.Context, lbls labels.Labels) (labels.Labels, error) {
// Find the tenant.
var tenant string
for _, label := range lbls {
if label.Name == lp.tenantLabelName {
tenant = label.Value
break
}
}
// Mutable labels are disabled when no tenant is found.
if tenant == "" {
return lbls, nil
}
builder := labels.NewBuilder(lbls)
// Search for mutable labels associated to these labels.
for _, label := range lbls {
isMutable, err := lp.IsMutableLabel(ctx, tenant, label.Name)
if err != nil {
return nil, err
}
// Remove mutable labels present in the input.
// It means the metric was written with labels that later became mutable.
if isMutable {
builder.Del(label.Name)
}
newMutableLabels, err := lp.labelProvider.GetMutable(ctx, tenant, label.Name, label.Value)
if err != nil {
if errors.Is(err, errNoResult) {
continue
}
return nil, err
}
for _, label := range newMutableLabels {
builder.Set(label.Name, label.Value)
}
}
return builder.Labels(), nil
}
// MutableLabelNames returns all the mutable label names possible for a tenant.
func (lp *LabelProcessor) MutableLabelNames(ctx context.Context, tenant string) ([]string, error) {
return lp.labelProvider.MutableLabelNames(ctx, tenant)
}