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:
parent
70bd5dad98
commit
4494abfce4
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue