Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support UTF-8 label matchers: Add new parser #3453

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
8a340c6
Add label matchers parser
grobinson-grafana Aug 9, 2023
4a61022
Comment out non-compliant tests
grobinson-grafana Aug 9, 2023
4e5c1ab
Fix unused lint
grobinson-grafana Aug 9, 2023
ac2c61b
Use t.Skip() to skip tests
grobinson-grafana Aug 9, 2023
18429bb
Feedback
grobinson-grafana Aug 9, 2023
5603b1e
Fix test
grobinson-grafana Aug 9, 2023
68c9724
Add docs to lexer.go and parse.go
grobinson-grafana Aug 9, 2023
ed7750d
Fix comment
grobinson-grafana Aug 14, 2023
e8bbe9b
Remove unused IsNone
grobinson-grafana Aug 14, 2023
f55bc8a
Update order of struct fields
grobinson-grafana Aug 14, 2023
c9eb963
Rewrite parseOpenParen and remoave accept
grobinson-grafana Aug 14, 2023
6f4db08
Fix err not returned from peekNext
grobinson-grafana Aug 14, 2023
7c6be41
Add benchmarks
grobinson-grafana Aug 15, 2023
593fa56
Use eof instead of -1
grobinson-grafana Aug 15, 2023
c21972d
Rename Ident to Unquoted
grobinson-grafana Aug 19, 2023
a00c8f6
Support unquoted outside of reserved characters
grobinson-grafana Aug 21, 2023
6f5f8b6
Add test for no input to lexer_test.go
grobinson-grafana Aug 21, 2023
e7e3223
Add tests for ParseMatcher
grobinson-grafana Aug 21, 2023
546fea5
Update compliance_test.go
grobinson-grafana Aug 21, 2023
af80340
Rename TokenNone to TokenEOF
grobinson-grafana Aug 22, 2023
e5b8cd0
Update comments in parse.go
grobinson-grafana Aug 22, 2023
0a87da7
Rename parseFn to parseFunc
grobinson-grafana Aug 22, 2023
2614b38
Use correct name for hasOpenParen
grobinson-grafana Aug 22, 2023
c950ebe
Rename parseOpenParen and parseCloseParen
grobinson-grafana Aug 22, 2023
d3f2bfe
Better errors when expecting EOF
grobinson-grafana Aug 22, 2023
0e7617f
Remove input from parse.go
grobinson-grafana Aug 22, 2023
a371c2a
Fix lint
grobinson-grafana Aug 22, 2023
0020634
Fix lint again
grobinson-grafana Aug 22, 2023
93f876d
Add fuzz tests
grobinson-grafana Aug 24, 2023
4add08c
Don't skip test with dashes
grobinson-grafana Aug 25, 2023
8686688
Add repl
grobinson-grafana Aug 25, 2023
ec244c0
Feedback
grobinson-grafana Aug 25, 2023
996b3d0
Add missing license to repl
grobinson-grafana Aug 25, 2023
a95d3ce
Use fmt.Println in repl
grobinson-grafana Aug 25, 2023
a76d9cc
Use os.Stderr on error
grobinson-grafana Aug 25, 2023
d297917
Remove repl for another PR
grobinson-grafana Aug 25, 2023
2122180
Add comments for emit, next, rewind and skip
grobinson-grafana Aug 29, 2023
fad8236
Fix bug when skipping two or more columns
grobinson-grafana Aug 29, 2023
2286467
Feedback
grobinson-grafana Aug 31, 2023
c67a5c1
Fix comment
grobinson-grafana Aug 31, 2023
3fafba3
Fix comments
grobinson-grafana Aug 31, 2023
c24b758
Remove duplicated errors from parse.go
grobinson-grafana Aug 31, 2023
ba09503
Remove check for open and closing braces in Matcher
grobinson-grafana Aug 31, 2023
222f85d
Make Unquote a method of Token
grobinson-grafana Aug 31, 2023
55e2cda
Make all code package private
grobinson-grafana Aug 31, 2023
4d7acef
Use position in errors
grobinson-grafana Aug 31, 2023
fca77b0
Fix unwrapped errors
grobinson-grafana Aug 31, 2023
c080f9d
Make errors package-private and remove function wrapping
grobinson-grafana Aug 31, 2023
47feed7
recover from panic on fatal error
grobinson-grafana Sep 1, 2023
ce06d33
Small non-functional changes to improve code structure and comments
grobinson-grafana Sep 1, 2023
41d4fb6
Fix mistake added in previous commit to accept
grobinson-grafana Sep 1, 2023
28c0d3a
Consistent use of t as token variable
grobinson-grafana Sep 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
393 changes: 393 additions & 0 deletions matchers/compliance/compliance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,393 @@
// Copyright 2023 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package compliance

import (
"reflect"
"testing"

"github.com/prometheus/alertmanager/matchers/parse"
"github.com/prometheus/alertmanager/pkg/labels"
)

func TestCompliance(t *testing.T) {
for _, tc := range []struct {
input string
want labels.Matchers
err string
skip bool
}{
{
input: `{}`,
want: labels.Matchers{},
skip: true,
},
{
input: `{foo='}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "'")
return append(ms, m)
}(),
skip: true,
},
{
input: "{foo=`}",
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "`")
return append(ms, m)
}(),
skip: true,
},
{
input: "{foo=\\\"}",
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\"")
return append(ms, m)
}(),
skip: true,
},
{
input: `{foo=bar}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
return append(ms, m)
}(),
},
{
input: `{foo="bar"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
return append(ms, m)
}(),
},
{
input: `{foo=~bar.*}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchRegexp, "foo", "bar.*")
return append(ms, m)
}(),
},
{
input: `{foo=~"bar.*"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchRegexp, "foo", "bar.*")
return append(ms, m)
}(),
},
{
input: `{foo!=bar}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchNotEqual, "foo", "bar")
return append(ms, m)
}(),
},
{
input: `{foo!="bar"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchNotEqual, "foo", "bar")
return append(ms, m)
}(),
},
{
input: `{foo!~bar.*}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchNotRegexp, "foo", "bar.*")
return append(ms, m)
}(),
},
{
input: `{foo!~"bar.*"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchNotRegexp, "foo", "bar.*")
return append(ms, m)
}(),
},
{
input: `{foo="bar", baz!="quux"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotEqual, "baz", "quux")
return append(ms, m, m2)
}(),
},
{
input: `{foo="bar", baz!~"quux.*"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotRegexp, "baz", "quux.*")
return append(ms, m, m2)
}(),
},
{
input: `{foo="bar",baz!~".*quux", derp="wat"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotRegexp, "baz", ".*quux")
m3, _ := labels.NewMatcher(labels.MatchEqual, "derp", "wat")
return append(ms, m, m2, m3)
}(),
},
{
input: `{foo="bar", baz!="quux", derp="wat"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotEqual, "baz", "quux")
m3, _ := labels.NewMatcher(labels.MatchEqual, "derp", "wat")
return append(ms, m, m2, m3)
}(),
},
{
input: `{foo="bar", baz!~".*quux.*", derp="wat"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotRegexp, "baz", ".*quux.*")
m3, _ := labels.NewMatcher(labels.MatchEqual, "derp", "wat")
return append(ms, m, m2, m3)
}(),
},
{
input: `{foo="bar", instance=~"some-api.*"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchRegexp, "instance", "some-api.*")
return append(ms, m, m2)
}(),
},
{
input: `{foo=""}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "")
return append(ms, m)
}(),
},
{
input: `{foo="bar,quux", job="job1"}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar,quux")
m2, _ := labels.NewMatcher(labels.MatchEqual, "job", "job1")
return append(ms, m, m2)
}(),
},
{
input: `{foo = "bar", dings != "bums", }`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotEqual, "dings", "bums")
return append(ms, m, m2)
}(),
},
{
input: `foo=bar,dings!=bums`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar")
m2, _ := labels.NewMatcher(labels.MatchNotEqual, "dings", "bums")
return append(ms, m, m2)
}(),
},
{
input: `{quote="She said: \"Hi, ladies! That's gender-neutral…\""}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "quote", `She said: "Hi, ladies! That's gender-neutral…"`)
return append(ms, m)
}(),
},
{
input: `statuscode=~"5.."`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchRegexp, "statuscode", "5..")
return append(ms, m)
}(),
},
{
input: `tricky=~~~`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchRegexp, "tricky", "~~")
return append(ms, m)
}(),
skip: true,
},
{
input: `trickier==\\=\=\"`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "trickier", `=\=\="`)
return append(ms, m)
}(),
skip: true,
},
{
input: `contains_quote != "\"" , contains_comma !~ "foo,bar" , `,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchNotEqual, "contains_quote", `"`)
m2, _ := labels.NewMatcher(labels.MatchNotRegexp, "contains_comma", "foo,bar")
return append(ms, m, m2)
}(),
},
{
input: `{foo=bar}}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar}")
return append(ms, m)
}(),
skip: true,
},
{
input: `{foo=bar}},}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar}}")
return append(ms, m)
}(),
skip: true,
},
{
input: `{foo=,bar=}}`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m1, _ := labels.NewMatcher(labels.MatchEqual, "foo", "")
m2, _ := labels.NewMatcher(labels.MatchEqual, "bar", "}")
return append(ms, m1, m2)
}(),
skip: true,
},
{
input: `job=`,
want: func() labels.Matchers {
m, _ := labels.NewMatcher(labels.MatchEqual, "job", "")
return labels.Matchers{m}
}(),
skip: true,
},
{
input: `{name-with-dashes = "bar"}`,
want: func() labels.Matchers {
m, _ := labels.NewMatcher(labels.MatchEqual, "name-with-dashes", "bar")
return labels.Matchers{m}
}(),
},
{
input: `{,}`,
err: "bad matcher format: ",
},
{
input: `job="value`,
err: `matcher value contains unescaped double quote: "value`,
},
{
input: `job=value"`,
err: `matcher value contains unescaped double quote: value"`,
},
{
input: `trickier==\\=\=\""`,
err: `matcher value contains unescaped double quote: =\\=\=\""`,
},
{
input: `contains_unescaped_quote = foo"bar`,
err: `matcher value contains unescaped double quote: foo"bar`,
},
{
input: `{foo=~"invalid[regexp"}`,
err: "error parsing regexp: missing closing ]: `[regexp)$`",
},
// Double escaped strings.
{
input: `"{foo=\"bar"}`,
err: `bad matcher format: "{foo=\"bar"`,
},
{
input: `"foo=\"bar"`,
err: `bad matcher format: "foo=\"bar"`,
},
{
input: `"foo=\"bar\""`,
err: `bad matcher format: "foo=\"bar\""`,
},
{
input: `"foo=\"bar\"`,
err: `bad matcher format: "foo=\"bar\"`,
},
{
input: `"{foo=\"bar\"}"`,
err: `bad matcher format: "{foo=\"bar\"}"`,
},
{
input: `"foo="bar""`,
err: `bad matcher format: "foo="bar""`,
},
{
input: `{{foo=`,
err: `bad matcher format: {foo=`,
},
{
input: `{foo=`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "")
return append(ms, m)
}(),
skip: true,
},
{
input: `{foo=}b`,
want: func() labels.Matchers {
ms := labels.Matchers{}
m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "}b")
return append(ms, m)
}(),
skip: true,
},
} {
t.Run(tc.input, func(t *testing.T) {
if tc.skip {
t.Skip()
}
got, err := parse.Matchers(tc.input)
if err != nil && tc.err == "" {
t.Fatalf("got error where none expected: %v", err)
}
if err == nil && tc.err != "" {
t.Fatalf("expected error but got none: %v", tc.err)
}
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("labels not equal:\ngot %#v\nwant %#v", got, tc.want)
}
})
}
}
Loading