diff --git a/matchers/adapter/parse.go b/matchers/adapter/parse.go index 22dcc5b34b..ead6848056 100644 --- a/matchers/adapter/parse.go +++ b/matchers/adapter/parse.go @@ -30,10 +30,10 @@ var ( ParseMatchers = FallbackMatchersParser(log.NewNopLogger()) ) -// MatcherParser is an interface for parsing individual matchers. +// MatcherParser is an interface for parsing a single matcher in the input string. type MatcherParser func(s string) (*labels.Matcher, error) -// MatchersParser is an interface for parsing a series of zero or more matchers. +// MatchersParser is an interface for parsing one or more matchers in the input string. type MatchersParser func(s string) (labels.Matchers, error) // OldMatcherParser uses the old pkg/labels parser to parse the matcher in @@ -51,7 +51,7 @@ func OldMatcherParser(l log.Logger) MatcherParser { } // OldMatchersParser uses the old pkg/labels parser to parse zero or more -// matchers in the string. It returns an error if the input is invalid. +// matchers in the string. It returns an error if the input is invalid. func OldMatchersParser(l log.Logger) MatchersParser { return func(s string) (labels.Matchers, error) { level.Debug(l).Log( @@ -63,27 +63,27 @@ func OldMatchersParser(l log.Logger) MatchersParser { } } -// FallbackMatchersParser uses the new matchers/parse parser to parse the -// matcher in the input string. If this fails it falls back to the old -// pkg/labels parser and emits a warning log line. -func FallbackMatchersParser(l log.Logger) MatchersParser { - return func(s string) (labels.Matchers, error) { +// FallbackMatcherParser uses the new matchers/parse parser to parse +// zero or more matchers in the string. If this fails it falls back to +// the old pkg/labels parser and emits a warning log line. +func FallbackMatcherParser(l log.Logger) MatcherParser { + return func(s string) (*labels.Matcher, error) { var ( - m []*labels.Matcher + m *labels.Matcher err error invalidErr error ) level.Debug(l).Log( "msg", - "Parsing matchers with new parser", - "matchers", + "Parsing matcher with new parser", + "matcher", s, ) - m, err = parse.Parse(s) + m, err = parse.Matcher(s) if err != nil { // The input is not valid in the old pkg/labels parser either, // it cannot be valid input. - m, invalidErr = labels.ParseMatchers(s) + m, invalidErr = labels.ParseMatcher(s) if invalidErr != nil { return nil, invalidErr } @@ -92,7 +92,7 @@ func FallbackMatchersParser(l log.Logger) MatchersParser { level.Warn(l).Log( "msg", "Failed to parse input with matchers/parse, falling back to pkg/labels parser", - "matchers", + "matcher", s, "err", err, @@ -102,27 +102,27 @@ func FallbackMatchersParser(l log.Logger) MatchersParser { } } -// FallbackMatcherParser uses the new matchers/parse parser to parse -// zero or more matchers in the string. If this fails it falls back to -// the old pkg/labels parser and emits a warning log line. -func FallbackMatcherParser(l log.Logger) MatcherParser { - return func(s string) (*labels.Matcher, error) { +// FallbackMatchersParser uses the new matchers/parse parser to parse the +// matcher in the input string. If this fails it falls back to the old +// pkg/labels parser and emits a warning log line. +func FallbackMatchersParser(l log.Logger) MatchersParser { + return func(s string) (labels.Matchers, error) { var ( - m *labels.Matcher + m []*labels.Matcher err error invalidErr error ) level.Debug(l).Log( "msg", - "Parsing matcher with new parser", - "matcher", + "Parsing matchers with new parser", + "matchers", s, ) - m, err = parse.ParseMatcher(s) + m, err = parse.Matchers(s) if err != nil { // The input is not valid in the old pkg/labels parser either, // it cannot be valid input. - m, invalidErr = labels.ParseMatcher(s) + m, invalidErr = labels.ParseMatchers(s) if invalidErr != nil { return nil, invalidErr } @@ -131,7 +131,7 @@ func FallbackMatcherParser(l log.Logger) MatcherParser { level.Warn(l).Log( "msg", "Failed to parse input with matchers/parse, falling back to pkg/labels parser", - "matcher", + "matchers", s, "err", err, diff --git a/matchers/adapter/parse_test.go b/matchers/adapter/parse_test.go new file mode 100644 index 0000000000..8b37f63a44 --- /dev/null +++ b/matchers/adapter/parse_test.go @@ -0,0 +1,98 @@ +package adapter + +import ( + "testing" + + "github.com/go-kit/log" + "github.com/prometheus/alertmanager/pkg/labels" + "github.com/stretchr/testify/require" +) + +func TestFallbackMatcherParser(t *testing.T) { + tests := []struct { + name string + input string + expected *labels.Matcher + err string + }{{ + name: "is accepted in both", + input: "foo=bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + }, { + name: "is accepted in new parser but not old", + input: "foo🙂=bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), + }, { + name: "is accepted in old parser but not new", + input: "foo=!bar", + expected: mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), + }, { + name: "is accepted in neither", + input: "foo!bar", + err: "bad matcher format: foo!bar", + }} + f := FallbackMatcherParser(log.NewNopLogger()) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matcher, err := f(test.input) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.Nil(t, err) + require.EqualValues(t, test.expected, matcher) + } + }) + } +} + +func TestFallbackMatchersParser(t *testing.T) { + tests := []struct { + name string + input string + expected labels.Matchers + err string + }{{ + name: "is accepted in both", + input: "{foo=bar,bar=baz}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "baz"), + }, + }, { + name: "is accepted in new parser but not old", + input: "{foo🙂=bar,bar=baz🙂}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo🙂", "bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "baz🙂"), + }, + }, { + name: "is accepted in old parser but not new", + input: "{foo=!bar,bar=$baz}", + expected: labels.Matchers{ + mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), + mustNewMatcher(t, labels.MatchEqual, "bar", "$baz"), + }, + }, { + name: "is accepted in neither", + input: "{foo!bar}", + err: "bad matcher format: foo!bar", + }} + f := FallbackMatchersParser(log.NewNopLogger()) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matchers, err := f(test.input) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.Nil(t, err) + require.EqualValues(t, test.expected, matchers) + } + }) + } +} + +func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labels.Matcher { + m, err := labels.NewMatcher(op, name, value) + require.NoError(t, err) + return m +}