Skip to content

Commit 4494abf

Browse files
Fix UTF-8 not supported in group_by (#3619)
* Fix UTF-8 not supported in group_by This commit fixes missing UTF-8 support in the group_by for routes. Signed-off-by: George Robinson <george.robinson@grafana.com> --------- Signed-off-by: George Robinson <george.robinson@grafana.com>
1 parent 70bd5da commit 4494abf

File tree

4 files changed

+93
-3
lines changed

4 files changed

+93
-3
lines changed

config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,7 @@ func (r *Route) UnmarshalYAML(unmarshal func(interface{}) error) error {
814814
r.GroupByAll = true
815815
} else {
816816
labelName := model.LabelName(l)
817-
if !labelName.IsValid() {
817+
if !compat.IsValidLabelName(labelName) {
818818
return fmt.Errorf("invalid label name %q in group_by list", l)
819819
}
820820
r.GroupBy = append(r.GroupBy, labelName)

matchers/compat/parse.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,28 @@ package compat
1616
import (
1717
"fmt"
1818
"strings"
19+
"unicode/utf8"
1920

2021
"github.com/go-kit/log"
2122
"github.com/go-kit/log/level"
23+
"github.com/prometheus/common/model"
2224

2325
"github.com/prometheus/alertmanager/featurecontrol"
2426
"github.com/prometheus/alertmanager/matchers/parse"
2527
"github.com/prometheus/alertmanager/pkg/labels"
2628
)
2729

2830
var (
29-
parseMatcher = classicMatcherParser(log.NewNopLogger())
30-
parseMatchers = classicMatchersParser(log.NewNopLogger())
31+
isValidLabelName = isValidClassicLabelName(log.NewNopLogger())
32+
parseMatcher = classicMatcherParser(log.NewNopLogger())
33+
parseMatchers = classicMatchersParser(log.NewNopLogger())
3134
)
3235

36+
// IsValidLabelName returns true if the string is a valid label name.
37+
func IsValidLabelName(name model.LabelName) bool {
38+
return isValidLabelName(name)
39+
}
40+
3341
type matcherParser func(s string) (*labels.Matcher, error)
3442

3543
type matchersParser func(s string) (labels.Matchers, error)
@@ -49,12 +57,15 @@ func Matchers(s string) (labels.Matchers, error) {
4957
// InitFromFlags initializes the compat package from the flagger.
5058
func InitFromFlags(l log.Logger, f featurecontrol.Flagger) {
5159
if f.ClassicMode() {
60+
isValidLabelName = isValidClassicLabelName(l)
5261
parseMatcher = classicMatcherParser(l)
5362
parseMatchers = classicMatchersParser(l)
5463
} else if f.UTF8StrictMode() {
64+
isValidLabelName = isValidUTF8LabelName(l)
5565
parseMatcher = utf8MatcherParser(l)
5666
parseMatchers = utf8MatchersParser(l)
5767
} else {
68+
isValidLabelName = isValidUTF8LabelName(l)
5869
parseMatcher = fallbackMatcherParser(l)
5970
parseMatchers = fallbackMatchersParser(l)
6071
}
@@ -166,3 +177,17 @@ func fallbackMatchersParser(l log.Logger) matchersParser {
166177
return m, nil
167178
}
168179
}
180+
181+
// isValidClassicLabelName returns true if the string is a valid classic label name.
182+
func isValidClassicLabelName(_ log.Logger) func(model.LabelName) bool {
183+
return func(name model.LabelName) bool {
184+
return name.IsValid()
185+
}
186+
}
187+
188+
// isValidUTF8LabelName returns true if the string is a valid UTF-8 label name.
189+
func isValidUTF8LabelName(_ log.Logger) func(model.LabelName) bool {
190+
return func(name model.LabelName) bool {
191+
return utf8.ValidString(string(name))
192+
}
193+
}

matchers/compat/parse_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
"github.com/go-kit/log"
20+
"github.com/prometheus/common/model"
2021
"github.com/stretchr/testify/require"
2122

2223
"github.com/prometheus/alertmanager/pkg/labels"
@@ -110,3 +111,65 @@ func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labe
110111
require.NoError(t, err)
111112
return m
112113
}
114+
115+
func TestIsValidClassicLabelName(t *testing.T) {
116+
tests := []struct {
117+
name string
118+
input model.LabelName
119+
expected bool
120+
}{{
121+
name: "is accepted",
122+
input: "foo",
123+
expected: true,
124+
}, {
125+
name: "is also accepted",
126+
input: "_foo1",
127+
expected: true,
128+
}, {
129+
name: "is not accepted",
130+
input: "0foo",
131+
expected: false,
132+
}, {
133+
name: "is also not accepted",
134+
input: "foo🙂",
135+
expected: false,
136+
}}
137+
138+
for _, test := range tests {
139+
fn := isValidClassicLabelName(log.NewNopLogger())
140+
t.Run(test.name, func(t *testing.T) {
141+
require.Equal(t, test.expected, fn(test.input))
142+
})
143+
}
144+
}
145+
146+
func TestIsValidUTF8LabelName(t *testing.T) {
147+
tests := []struct {
148+
name string
149+
input model.LabelName
150+
expected bool
151+
}{{
152+
name: "is accepted",
153+
input: "foo",
154+
expected: true,
155+
}, {
156+
name: "is also accepted",
157+
input: "_foo1",
158+
expected: true,
159+
}, {
160+
name: "is accepted in UTF-8",
161+
input: "0foo",
162+
expected: true,
163+
}, {
164+
name: "is also accepted with UTF-8",
165+
input: "foo🙂",
166+
expected: true,
167+
}}
168+
169+
for _, test := range tests {
170+
fn := isValidUTF8LabelName(log.NewNopLogger())
171+
t.Run(test.name, func(t *testing.T) {
172+
require.Equal(t, test.expected, fn(test.input))
173+
})
174+
}
175+
}

test/with_api_v2/acceptance/utf8_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ route:
280280
- receiver: webhook
281281
matchers:
282282
- foo🙂=bar
283+
group_by:
284+
- foo🙂
283285
group_wait: 1s
284286
receivers:
285287
- name: default

0 commit comments

Comments
 (0)