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>
This commit is contained in:
George Robinson 2023-11-24 11:26:39 +00:00 committed by GitHub
parent 70bd5dad98
commit 4494abfce4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 3 deletions

View File

@ -814,7 +814,7 @@ func (r *Route) UnmarshalYAML(unmarshal func(interface{}) error) error {
r.GroupByAll = true r.GroupByAll = true
} else { } else {
labelName := model.LabelName(l) labelName := model.LabelName(l)
if !labelName.IsValid() { if !compat.IsValidLabelName(labelName) {
return fmt.Errorf("invalid label name %q in group_by list", l) return fmt.Errorf("invalid label name %q in group_by list", l)
} }
r.GroupBy = append(r.GroupBy, labelName) r.GroupBy = append(r.GroupBy, labelName)

View File

@ -16,9 +16,11 @@ package compat
import ( import (
"fmt" "fmt"
"strings" "strings"
"unicode/utf8"
"github.com/go-kit/log" "github.com/go-kit/log"
"github.com/go-kit/log/level" "github.com/go-kit/log/level"
"github.com/prometheus/common/model"
"github.com/prometheus/alertmanager/featurecontrol" "github.com/prometheus/alertmanager/featurecontrol"
"github.com/prometheus/alertmanager/matchers/parse" "github.com/prometheus/alertmanager/matchers/parse"
@ -26,10 +28,16 @@ import (
) )
var ( var (
isValidLabelName = isValidClassicLabelName(log.NewNopLogger())
parseMatcher = classicMatcherParser(log.NewNopLogger()) parseMatcher = classicMatcherParser(log.NewNopLogger())
parseMatchers = classicMatchersParser(log.NewNopLogger()) parseMatchers = classicMatchersParser(log.NewNopLogger())
) )
// IsValidLabelName returns true if the string is a valid label name.
func IsValidLabelName(name model.LabelName) bool {
return isValidLabelName(name)
}
type matcherParser func(s string) (*labels.Matcher, error) type matcherParser func(s string) (*labels.Matcher, error)
type matchersParser func(s string) (labels.Matchers, error) type matchersParser func(s string) (labels.Matchers, error)
@ -49,12 +57,15 @@ func Matchers(s string) (labels.Matchers, error) {
// InitFromFlags initializes the compat package from the flagger. // InitFromFlags initializes the compat package from the flagger.
func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { func InitFromFlags(l log.Logger, f featurecontrol.Flagger) {
if f.ClassicMode() { if f.ClassicMode() {
isValidLabelName = isValidClassicLabelName(l)
parseMatcher = classicMatcherParser(l) parseMatcher = classicMatcherParser(l)
parseMatchers = classicMatchersParser(l) parseMatchers = classicMatchersParser(l)
} else if f.UTF8StrictMode() { } else if f.UTF8StrictMode() {
isValidLabelName = isValidUTF8LabelName(l)
parseMatcher = utf8MatcherParser(l) parseMatcher = utf8MatcherParser(l)
parseMatchers = utf8MatchersParser(l) parseMatchers = utf8MatchersParser(l)
} else { } else {
isValidLabelName = isValidUTF8LabelName(l)
parseMatcher = fallbackMatcherParser(l) parseMatcher = fallbackMatcherParser(l)
parseMatchers = fallbackMatchersParser(l) parseMatchers = fallbackMatchersParser(l)
} }
@ -166,3 +177,17 @@ func fallbackMatchersParser(l log.Logger) matchersParser {
return m, nil return m, nil
} }
} }
// isValidClassicLabelName returns true if the string is a valid classic label name.
func isValidClassicLabelName(_ log.Logger) func(model.LabelName) bool {
return func(name model.LabelName) bool {
return name.IsValid()
}
}
// isValidUTF8LabelName returns true if the string is a valid UTF-8 label name.
func isValidUTF8LabelName(_ log.Logger) func(model.LabelName) bool {
return func(name model.LabelName) bool {
return utf8.ValidString(string(name))
}
}

View File

@ -17,6 +17,7 @@ import (
"testing" "testing"
"github.com/go-kit/log" "github.com/go-kit/log"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/pkg/labels"
@ -110,3 +111,65 @@ func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labe
require.NoError(t, err) require.NoError(t, err)
return m return m
} }
func TestIsValidClassicLabelName(t *testing.T) {
tests := []struct {
name string
input model.LabelName
expected bool
}{{
name: "is accepted",
input: "foo",
expected: true,
}, {
name: "is also accepted",
input: "_foo1",
expected: true,
}, {
name: "is not accepted",
input: "0foo",
expected: false,
}, {
name: "is also not accepted",
input: "foo🙂",
expected: false,
}}
for _, test := range tests {
fn := isValidClassicLabelName(log.NewNopLogger())
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, fn(test.input))
})
}
}
func TestIsValidUTF8LabelName(t *testing.T) {
tests := []struct {
name string
input model.LabelName
expected bool
}{{
name: "is accepted",
input: "foo",
expected: true,
}, {
name: "is also accepted",
input: "_foo1",
expected: true,
}, {
name: "is accepted in UTF-8",
input: "0foo",
expected: true,
}, {
name: "is also accepted with UTF-8",
input: "foo🙂",
expected: true,
}}
for _, test := range tests {
fn := isValidUTF8LabelName(log.NewNopLogger())
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, fn(test.input))
})
}
}

View File

@ -280,6 +280,8 @@ route:
- receiver: webhook - receiver: webhook
matchers: matchers:
- foo🙂=bar - foo🙂=bar
group_by:
- foo🙂
group_wait: 1s group_wait: 1s
receivers: receivers:
- name: default - name: default