diff --git a/api/v2/api.go b/api/v2/api.go index aa6c8a72ab..4a071e1d60 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -45,8 +45,8 @@ import ( "github.com/prometheus/alertmanager/cluster" "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/silence" "github.com/prometheus/alertmanager/silence/silencepb" @@ -426,7 +426,7 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams return alertgroup_ops.NewGetAlertGroupsOK().WithPayload(res) } -func (api *API) alertFilter(matchers []*labels.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool { +func (api *API) alertFilter(matchers []*matcher.Matcher, silenced, inhibited, active bool) func(a *types.Alert, now time.Time) bool { return func(a *types.Alert, now time.Time) bool { if !a.EndsAt.IsZero() && a.EndsAt.Before(now) { return false @@ -472,7 +472,7 @@ func receiversMatchFilter(receivers []string, filter *regexp.Regexp) bool { return false } -func alertMatchesFilterLabels(a *prometheus_model.Alert, matchers []*labels.Matcher) bool { +func alertMatchesFilterLabels(a *prometheus_model.Alert, matchers []*matcher.Matcher) bool { sms := make(map[string]string) for name, value := range a.Labels { sms[string(name)] = string(value) @@ -480,11 +480,11 @@ func alertMatchesFilterLabels(a *prometheus_model.Alert, matchers []*labels.Matc return matchFilterLabels(matchers, sms) } -func matchFilterLabels(matchers []*labels.Matcher, sms map[string]string) bool { +func matchFilterLabels(matchers []*matcher.Matcher, sms map[string]string) bool { for _, m := range matchers { v, prs := sms[m.Name] switch m.Type { - case labels.MatchNotRegexp, labels.MatchNotEqual: + case matcher.MatchNotRegexp, matcher.MatchNotEqual: if m.Value == "" && prs { continue } @@ -578,16 +578,16 @@ func SortSilences(sils open_api_models.GettableSilences) { // A silence matches a filter (list of matchers) if // for all matchers in the filter, there exists a matcher in the silence // such that their names, types, and values are equivalent. -func CheckSilenceMatchesFilterLabels(s *silencepb.Silence, matchers []*labels.Matcher) bool { - for _, matcher := range matchers { +func CheckSilenceMatchesFilterLabels(s *silencepb.Silence, matchers []*matcher.Matcher) bool { + for _, m1 := range matchers { found := false - for _, m := range s.Matchers { - if matcher.Name == m.Name && - (matcher.Type == labels.MatchEqual && m.Type == silencepb.Matcher_EQUAL || - matcher.Type == labels.MatchRegexp && m.Type == silencepb.Matcher_REGEXP || - matcher.Type == labels.MatchNotEqual && m.Type == silencepb.Matcher_NOT_EQUAL || - matcher.Type == labels.MatchNotRegexp && m.Type == silencepb.Matcher_NOT_REGEXP) && - matcher.Value == m.Pattern { + for _, m2 := range s.Matchers { + if m1.Name == m2.Name && + (m1.Type == matcher.MatchEqual && m2.Type == silencepb.Matcher_EQUAL || + m1.Type == matcher.MatchRegexp && m2.Type == silencepb.Matcher_REGEXP || + m1.Type == matcher.MatchNotEqual && m2.Type == silencepb.Matcher_NOT_EQUAL || + m1.Type == matcher.MatchNotRegexp && m2.Type == silencepb.Matcher_NOT_REGEXP) && + m1.Value == m2.Pattern { found = true break } @@ -673,8 +673,8 @@ func (api *API) postSilencesHandler(params silence_ops.PostSilencesParams) middl }) } -func parseFilter(filter []string) ([]*labels.Matcher, error) { - matchers := make([]*labels.Matcher, 0, len(filter)) +func parseFilter(filter []string) ([]*matcher.Matcher, error) { + matchers := make([]*matcher.Matcher, 0, len(filter)) for _, matcherString := range filter { matcher, err := compat.Matcher(matcherString, "api") if err != nil { diff --git a/api/v2/api_test.go b/api/v2/api_test.go index 438e6f44d4..511bf09a1f 100644 --- a/api/v2/api_test.go +++ b/api/v2/api_test.go @@ -35,7 +35,7 @@ import ( receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver" silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/silence" "github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/types" @@ -376,70 +376,70 @@ func postSilences( func TestCheckSilenceMatchesFilterLabels(t *testing.T) { type test struct { silenceMatchers []*silencepb.Matcher - filterMatchers []*labels.Matcher + filterMatchers []*matcher.Matcher expected bool } tests := []test{ { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchEqual)}, true, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "novalue", labels.MatchEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "novalue", matcher.MatchEqual)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "(foo|bar)", silencepb.Matcher_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "(foo|bar)", labels.MatchRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "(foo|bar)", matcher.MatchRegexp)}, true, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "foo", silencepb.Matcher_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "(foo|bar)", labels.MatchRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "(foo|bar)", matcher.MatchRegexp)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchRegexp)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchEqual)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_NOT_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotEqual)}, true, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_NOT_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotRegexp)}, true, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotEqual)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotRegexp)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_NOT_EQUAL)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotRegexp)}, false, }, { []*silencepb.Matcher{createSilenceMatcher(t, "label", "value", silencepb.Matcher_NOT_REGEXP)}, - []*labels.Matcher{createLabelMatcher(t, "label", "value", labels.MatchNotEqual)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "value", matcher.MatchNotEqual)}, false, }, { @@ -447,7 +447,7 @@ func TestCheckSilenceMatchesFilterLabels(t *testing.T) { createSilenceMatcher(t, "label", "(foo|bar)", silencepb.Matcher_REGEXP), createSilenceMatcher(t, "label", "value", silencepb.Matcher_EQUAL), }, - []*labels.Matcher{createLabelMatcher(t, "label", "(foo|bar)", labels.MatchRegexp)}, + []*matcher.Matcher{createLabelMatcher(t, "label", "(foo|bar)", matcher.MatchRegexp)}, true, }, } @@ -512,34 +512,34 @@ func TestMatchFilterLabels(t *testing.T) { } testCases := []struct { - matcher labels.MatchType + matcher matcher.MatchType name string val string expected bool }{ - {labels.MatchEqual, "foo", "bar", true}, - {labels.MatchEqual, "baz", "", true}, - {labels.MatchEqual, "baz", "qux", false}, - {labels.MatchEqual, "baz", "qux|", false}, - {labels.MatchRegexp, "foo", "bar", true}, - {labels.MatchRegexp, "baz", "", true}, - {labels.MatchRegexp, "baz", "qux", false}, - {labels.MatchRegexp, "baz", "qux|", true}, - {labels.MatchNotEqual, "foo", "bar", false}, - {labels.MatchNotEqual, "baz", "", false}, - {labels.MatchNotEqual, "baz", "qux", true}, - {labels.MatchNotEqual, "baz", "qux|", true}, - {labels.MatchNotRegexp, "foo", "bar", false}, - {labels.MatchNotRegexp, "baz", "", false}, - {labels.MatchNotRegexp, "baz", "qux", true}, - {labels.MatchNotRegexp, "baz", "qux|", false}, + {matcher.MatchEqual, "foo", "bar", true}, + {matcher.MatchEqual, "baz", "", true}, + {matcher.MatchEqual, "baz", "qux", false}, + {matcher.MatchEqual, "baz", "qux|", false}, + {matcher.MatchRegexp, "foo", "bar", true}, + {matcher.MatchRegexp, "baz", "", true}, + {matcher.MatchRegexp, "baz", "qux", false}, + {matcher.MatchRegexp, "baz", "qux|", true}, + {matcher.MatchNotEqual, "foo", "bar", false}, + {matcher.MatchNotEqual, "baz", "", false}, + {matcher.MatchNotEqual, "baz", "qux", true}, + {matcher.MatchNotEqual, "baz", "qux|", true}, + {matcher.MatchNotRegexp, "foo", "bar", false}, + {matcher.MatchNotRegexp, "baz", "", false}, + {matcher.MatchNotRegexp, "baz", "qux", true}, + {matcher.MatchNotRegexp, "baz", "qux|", false}, } for _, tc := range testCases { - m, err := labels.NewMatcher(tc.matcher, tc.name, tc.val) + m, err := matcher.NewMatcher(tc.matcher, tc.name, tc.val) require.NoError(t, err) - ms := []*labels.Matcher{m} + ms := []*matcher.Matcher{m} require.Equal(t, tc.expected, matchFilterLabels(ms, sms)) } } diff --git a/api/v2/testing.go b/api/v2/testing.go index 7665a19f67..a59efedb86 100644 --- a/api/v2/testing.go +++ b/api/v2/testing.go @@ -20,7 +20,7 @@ import ( "github.com/go-openapi/strfmt" open_api_models "github.com/prometheus/alertmanager/api/v2/models" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/silence/silencepb" ) @@ -57,9 +57,9 @@ func createSilenceMatcher(t *testing.T, name, pattern string, matcherType silenc } } -func createLabelMatcher(t *testing.T, name, value string, matchType labels.MatchType) *labels.Matcher { +func createLabelMatcher(t *testing.T, name, value string, matchType matcher.MatchType) *matcher.Matcher { t.Helper() - matcher, _ := labels.NewMatcher(matchType, name, value) + matcher, _ := matcher.NewMatcher(matchType, name, value) return matcher } diff --git a/cli/alert_add.go b/cli/alert_add.go index 2890946c24..94ec132e21 100644 --- a/cli/alert_add.go +++ b/cli/alert_add.go @@ -25,8 +25,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/alert" "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" ) type alertAddCmd struct { @@ -84,26 +84,26 @@ func (a *alertAddCmd) addAlert(ctx context.Context, _ *kingpin.ParseContext) err ls := make(models.LabelSet, len(a.labels)) for _, l := range a.labels { - matcher, err := compat.Matcher(l, "cli") + m, err := compat.Matcher(l, "cli") if err != nil { return err } - if matcher.Type != labels.MatchEqual { + if m.Type != matcher.MatchEqual { return errors.New("labels must be specified as key=value pairs") } - ls[matcher.Name] = matcher.Value + ls[m.Name] = m.Value } annotations := make(models.LabelSet, len(a.annotations)) for _, a := range a.annotations { - matcher, err := compat.Matcher(a, "cli") + m, err := compat.Matcher(a, "cli") if err != nil { return err } - if matcher.Type != labels.MatchEqual { + if m.Type != matcher.MatchEqual { return errors.New("annotations must be specified as key=value pairs") } - annotations[matcher.Name] = matcher.Value + annotations[m.Name] = m.Value } var startsAt, endsAt time.Time diff --git a/cli/format/format.go b/cli/format/format.go index 7bab337808..1d1c638c31 100644 --- a/cli/format/format.go +++ b/cli/format/format.go @@ -21,7 +21,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/prometheus/alertmanager/api/v2/models" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) const DefaultDateFormat = "2006-01-02 15:04:05 MST" @@ -48,8 +48,8 @@ func FormatDate(input strfmt.DateTime) string { return time.Time(input).Format(*dateFormat) } -func labelsMatcher(m models.Matcher) *labels.Matcher { - var t labels.MatchType +func labelsMatcher(m models.Matcher) *matcher.Matcher { + var t matcher.MatchType // Support for older alertmanager releases, which did not support isEqual. if m.IsEqual == nil { isEqual := true @@ -57,14 +57,14 @@ func labelsMatcher(m models.Matcher) *labels.Matcher { } switch { case !*m.IsRegex && *m.IsEqual: - t = labels.MatchEqual + t = matcher.MatchEqual case !*m.IsRegex && !*m.IsEqual: - t = labels.MatchNotEqual + t = matcher.MatchNotEqual case *m.IsRegex && *m.IsEqual: - t = labels.MatchRegexp + t = matcher.MatchRegexp case *m.IsRegex && !*m.IsEqual: - t = labels.MatchNotRegexp + t = matcher.MatchNotRegexp } - return &labels.Matcher{Type: t, Name: *m.Name, Value: *m.Value} + return &matcher.Matcher{Type: t, Name: *m.Name, Value: *m.Value} } diff --git a/cli/format/format_extended.go b/cli/format/format_extended.go index 3870cc7db9..afcaa6697b 100644 --- a/cli/format/format_extended.go +++ b/cli/format/format_extended.go @@ -22,7 +22,7 @@ import ( "text/tabwriter" "github.com/prometheus/alertmanager/api/v2/models" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) type ExtendedFormatter struct { @@ -131,7 +131,7 @@ func extendedFormatAnnotations(labels models.LabelSet) string { } func extendedFormatMatchers(matchers models.Matchers) string { - lms := labels.Matchers{} + lms := matcher.Matchers{} for _, matcher := range matchers { lms = append(lms, labelsMatcher(*matcher)) } diff --git a/cli/silence_add.go b/cli/silence_add.go index e23d1fcc86..bfc2d853bb 100644 --- a/cli/silence_add.go +++ b/cli/silence_add.go @@ -27,8 +27,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/silence" "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" ) func username() string { @@ -101,7 +101,7 @@ func (c *silenceAddCmd) add(ctx context.Context, _ *kingpin.ParseContext) error } } - matchers := make([]labels.Matcher, 0, len(c.matchers)) + matchers := make([]matcher.Matcher, 0, len(c.matchers)) for _, s := range c.matchers { m, err := compat.Matcher(s, "cli") if err != nil { diff --git a/cli/test_routing.go b/cli/test_routing.go index 163dce391b..073c1219de 100644 --- a/cli/test_routing.go +++ b/cli/test_routing.go @@ -24,8 +24,8 @@ import ( "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" ) const routingTestHelp = `Test alert routing @@ -84,14 +84,14 @@ func (c *routingShow) routingTestAction(ctx context.Context, _ *kingpin.ParseCon // Parse labels to LabelSet. ls := make(models.LabelSet, len(c.labels)) for _, l := range c.labels { - matcher, err := compat.Matcher(l, "cli") + m, err := compat.Matcher(l, "cli") if err != nil { kingpin.Fatalf("Failed to parse labels: %v\n", err) } - if matcher.Type != labels.MatchEqual { + if m.Type != matcher.MatchEqual { kingpin.Fatalf("%s\n", "Labels must be specified as key=value pairs") } - ls[matcher.Name] = matcher.Value + ls[m.Name] = m.Value } if c.debugTree { diff --git a/cli/utils.go b/cli/utils.go index 7215220af2..6257128a6a 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -26,7 +26,7 @@ import ( "github.com/prometheus/alertmanager/api/v2/client/general" "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) // getRemoteAlertmanagerConfigStatus returns status responsecontaining configuration from remote Alertmanager. @@ -79,25 +79,25 @@ func convertClientToCommonLabelSet(cls models.LabelSet) model.LabelSet { } // TypeMatchers only valid for when you are going to add a silence. -func TypeMatchers(matchers []labels.Matcher) models.Matchers { +func TypeMatchers(matchers []matcher.Matcher) models.Matchers { typeMatchers := make(models.Matchers, len(matchers)) - for i, matcher := range matchers { - typeMatchers[i] = TypeMatcher(matcher) + for i, m := range matchers { + typeMatchers[i] = TypeMatcher(m) } return typeMatchers } // TypeMatcher only valid for when you are going to add a silence. -func TypeMatcher(matcher labels.Matcher) *models.Matcher { - name := matcher.Name - value := matcher.Value +func TypeMatcher(m matcher.Matcher) *models.Matcher { + name := m.Name + value := m.Value typeMatcher := models.Matcher{ Name: &name, Value: &value, } - isEqual := (matcher.Type == labels.MatchEqual) || (matcher.Type == labels.MatchRegexp) - isRegex := (matcher.Type == labels.MatchRegexp) || (matcher.Type == labels.MatchNotRegexp) + isEqual := (m.Type == matcher.MatchEqual) || (m.Type == matcher.MatchRegexp) + isRegex := (m.Type == matcher.MatchRegexp) || (m.Type == matcher.MatchNotRegexp) typeMatcher.IsEqual = &isEqual typeMatcher.IsRegex = &isRegex return &typeMatcher diff --git a/config/config.go b/config/config.go index 4d694eefcc..90abcf7ed1 100644 --- a/config/config.go +++ b/config/config.go @@ -30,8 +30,8 @@ import ( "github.com/prometheus/common/model" "gopkg.in/yaml.v2" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/timeinterval" ) @@ -1024,7 +1024,7 @@ func (re Regexp) MarshalJSON() ([]byte, error) { // Matchers is label.Matchers with an added UnmarshalYAML method to implement the yaml.Unmarshaler interface // and MarshalYAML to implement the yaml.Marshaler interface. -type Matchers labels.Matchers +type Matchers matcher.Matchers // UnmarshalYAML implements the yaml.Unmarshaler interface for Matchers. func (m *Matchers) UnmarshalYAML(unmarshal func(interface{}) error) error { @@ -1039,7 +1039,7 @@ func (m *Matchers) UnmarshalYAML(unmarshal func(interface{}) error) error { } *m = append(*m, pm...) } - sort.Sort(labels.Matchers(*m)) + sort.Sort(matcher.Matchers(*m)) return nil } @@ -1065,7 +1065,7 @@ func (m *Matchers) UnmarshalJSON(data []byte) error { } *m = append(*m, pm...) } - sort.Sort(labels.Matchers(*m)) + sort.Sort(matcher.Matchers(*m)) return nil } diff --git a/dispatch/route.go b/dispatch/route.go index e174672d3f..2752149fd5 100644 --- a/dispatch/route.go +++ b/dispatch/route.go @@ -24,7 +24,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) // DefaultRouteOpts are the defaulting routing options which apply @@ -47,7 +47,7 @@ type Route struct { // Matchers an alert has to fulfill to match // this route. - Matchers labels.Matchers + Matchers matcher.Matchers // If true, an alert matches further routes on the same level. Continue bool @@ -91,26 +91,26 @@ func NewRoute(cr *config.Route, parent *Route) *Route { } // Build matchers. - var matchers labels.Matchers + var matchers matcher.Matchers // cr.Match will be deprecated. This for loop appends matchers. for ln, lv := range cr.Match { - matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv) + m, err := matcher.NewMatcher(matcher.MatchEqual, ln, lv) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - matchers = append(matchers, matcher) + matchers = append(matchers, m) } // cr.MatchRE will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.MatchRE { - matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String()) + m, err := matcher.NewMatcher(matcher.MatchRegexp, ln, lv.String()) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - matchers = append(matchers, matcher) + matchers = append(matchers, m) } // We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed. diff --git a/inhibit/inhibit.go b/inhibit/inhibit.go index 42e20242e4..58ab559454 100644 --- a/inhibit/inhibit.go +++ b/inhibit/inhibit.go @@ -24,7 +24,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/store" "github.com/prometheus/alertmanager/types" @@ -153,10 +153,10 @@ func (ih *Inhibitor) Mutes(lset model.LabelSet) bool { type InhibitRule struct { // The set of Filters which define the group of source alerts (which inhibit // the target alerts). - SourceMatchers labels.Matchers + SourceMatchers matcher.Matchers // The set of Filters which define the group of target alerts (which are // inhibited by the source alerts). - TargetMatchers labels.Matchers + TargetMatchers matcher.Matchers // A set of label names whose label values need to be identical in source and // target alerts in order for the inhibition to take effect. Equal map[model.LabelName]struct{} @@ -168,47 +168,47 @@ type InhibitRule struct { // NewInhibitRule returns a new InhibitRule based on a configuration definition. func NewInhibitRule(cr config.InhibitRule) *InhibitRule { var ( - sourcem labels.Matchers - targetm labels.Matchers + sourcem matcher.Matchers + targetm matcher.Matchers ) // cr.SourceMatch will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.SourceMatch { - matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv) + m, err := matcher.NewMatcher(matcher.MatchEqual, ln, lv) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - sourcem = append(sourcem, matcher) + sourcem = append(sourcem, m) } // cr.SourceMatchRE will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.SourceMatchRE { - matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String()) + m, err := matcher.NewMatcher(matcher.MatchRegexp, ln, lv.String()) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - sourcem = append(sourcem, matcher) + sourcem = append(sourcem, m) } // We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed. sourcem = append(sourcem, cr.SourceMatchers...) // cr.TargetMatch will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.TargetMatch { - matcher, err := labels.NewMatcher(labels.MatchEqual, ln, lv) + m, err := matcher.NewMatcher(matcher.MatchEqual, ln, lv) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - targetm = append(targetm, matcher) + targetm = append(targetm, m) } // cr.TargetMatchRE will be deprecated. This for loop appends regex matchers. for ln, lv := range cr.TargetMatchRE { - matcher, err := labels.NewMatcher(labels.MatchRegexp, ln, lv.String()) + m, err := matcher.NewMatcher(matcher.MatchRegexp, ln, lv.String()) if err != nil { // This error must not happen because the config already validates the yaml. panic(err) } - targetm = append(targetm, matcher) + targetm = append(targetm, m) } // We append the new-style matchers. This can be simplified once the deprecated matcher syntax is removed. targetm = append(targetm, cr.TargetMatchers...) diff --git a/inhibit/inhibit_bench_test.go b/inhibit/inhibit_bench_test.go index 2aa881ea32..1621ca54bb 100644 --- a/inhibit/inhibit_bench_test.go +++ b/inhibit/inhibit_bench_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/require" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/provider/mem" "github.com/prometheus/alertmanager/types" ) @@ -110,10 +110,10 @@ func allRulesMatchBenchmark(b *testing.B, numInhibitionRules, numInhibitingAlert newRuleFunc: func(idx int) config.InhibitRule { return config.InhibitRule{ SourceMatchers: config.Matchers{ - mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)), + mustNewMatcher(b, matcher.MatchEqual, "src", strconv.Itoa(idx)), }, TargetMatchers: config.Matchers{ - mustNewMatcher(b, labels.MatchEqual, "dst", "0"), + mustNewMatcher(b, matcher.MatchEqual, "dst", "0"), }, } }, @@ -153,10 +153,10 @@ func lastRuleMatchesBenchmark(b *testing.B, n int) benchmarkOptions { newRuleFunc: func(idx int) config.InhibitRule { return config.InhibitRule{ SourceMatchers: config.Matchers{ - mustNewMatcher(b, labels.MatchEqual, "src", strconv.Itoa(idx)), + mustNewMatcher(b, matcher.MatchEqual, "src", strconv.Itoa(idx)), }, TargetMatchers: config.Matchers{ - mustNewMatcher(b, labels.MatchEqual, "dst", "0"), + mustNewMatcher(b, matcher.MatchEqual, "dst", "0"), }, } }, @@ -224,8 +224,8 @@ func benchmarkFromOptions(opts benchmarkOptions) ([]types.Alert, []config.Inhibi return alerts, rules } -func mustNewMatcher(b *testing.B, op labels.MatchType, name, value string) *labels.Matcher { - m, err := labels.NewMatcher(op, name, value) +func mustNewMatcher(b *testing.B, op matcher.MatchType, name, value string) *matcher.Matcher { + m, err := matcher.NewMatcher(op, name, value) require.NoError(b, err) return m } diff --git a/inhibit/inhibit_test.go b/inhibit/inhibit_test.go index 24089a671d..00c442e1fe 100644 --- a/inhibit/inhibit_test.go +++ b/inhibit/inhibit_test.go @@ -22,7 +22,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/store" "github.com/prometheus/alertmanager/types" @@ -238,13 +238,13 @@ func TestInhibitRuleMatchers(t *testing.T) { t.Parallel() rule1 := config.InhibitRule{ - SourceMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s1", Value: "1"}}, - TargetMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchNotEqual, Name: "t1", Value: "1"}}, + SourceMatchers: config.Matchers{&matcher.Matcher{Type: matcher.MatchEqual, Name: "s1", Value: "1"}}, + TargetMatchers: config.Matchers{&matcher.Matcher{Type: matcher.MatchNotEqual, Name: "t1", Value: "1"}}, Equal: model.LabelNames{"e"}, } rule2 := config.InhibitRule{ - SourceMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "s2", Value: "1"}}, - TargetMatchers: config.Matchers{&labels.Matcher{Type: labels.MatchEqual, Name: "t2", Value: "1"}}, + SourceMatchers: config.Matchers{&matcher.Matcher{Type: matcher.MatchEqual, Name: "s2", Value: "1"}}, + TargetMatchers: config.Matchers{&matcher.Matcher{Type: matcher.MatchEqual, Name: "t2", Value: "1"}}, Equal: model.LabelNames{"e"}, } diff --git a/matcher/compat/parse.go b/matcher/compat/parse.go index 14aeb5a2ae..86303b778c 100644 --- a/matcher/compat/parse.go +++ b/matcher/compat/parse.go @@ -24,8 +24,9 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/featurecontrol" + "github.com/prometheus/alertmanager/matcher" + "github.com/prometheus/alertmanager/matcher/oldparse" "github.com/prometheus/alertmanager/matcher/parse" - "github.com/prometheus/alertmanager/pkg/labels" ) var ( @@ -39,19 +40,19 @@ func IsValidLabelName(name model.LabelName) bool { return isValidLabelName(name) } -type ParseMatcher func(input, origin string) (*labels.Matcher, error) +type ParseMatcher func(input, origin string) (*matcher.Matcher, error) -type ParseMatchers func(input, origin string) (labels.Matchers, error) +type ParseMatchers func(input, origin string) (matcher.Matchers, error) // Matcher parses the matcher in the input string. It returns an error // if the input is invalid or contains two or more matchers. -func Matcher(input, origin string) (*labels.Matcher, error) { +func Matcher(input, origin string) (*matcher.Matcher, error) { return parseMatcher(input, origin) } // Matchers parses one or more matchers in the input string. It returns // an error if the input is invalid. -func Matchers(input, origin string) (labels.Matchers, error) { +func Matchers(input, origin string) (matcher.Matchers, error) { return parseMatchers(input, origin) } @@ -72,28 +73,29 @@ func InitFromFlags(l log.Logger, f featurecontrol.Flagger) { } } -// ClassicMatcherParser uses the pkg/labels parser to parse the matcher in -// the input string. +// ClassicMatcherParser uses the matchers/oldparse parser to parse the matcher +// in the input string. func ClassicMatcherParser(l log.Logger) ParseMatcher { - return func(input, origin string) (matcher *labels.Matcher, err error) { + return func(input, origin string) (matcher *matcher.Matcher, err error) { level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) - return labels.ParseMatcher(input) + return oldparse.ParseMatcher(input) } } -// ClassicMatchersParser uses the pkg/labels parser to parse zero or more +// ClassicMatchersParser uses the matchers/oldparse parser to parse zero or more // matchers in the input string. It returns an error if the input is invalid. func ClassicMatchersParser(l log.Logger) ParseMatchers { - return func(input, origin string) (matchers labels.Matchers, err error) { + return func(input, origin string) (matchers matcher.Matchers, err error) { level.Debug(l).Log("msg", "Parsing with classic matchers parser", "input", input, "origin", origin) - return labels.ParseMatchers(input) + return oldparse.ParseMatchers(input) } } // UTF8MatcherParser uses the new matcher/parse parser to parse the matcher -// in the input string. If this fails it does not revert to the pkg/labels parser. +// in the input string. If this fails it does not revert to the +// matchers/oldparse parser. func UTF8MatcherParser(l log.Logger) ParseMatcher { - return func(input, origin string) (matcher *labels.Matcher, err error) { + return func(input, origin string) (matcher *matcher.Matcher, err error) { level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) @@ -104,19 +106,19 @@ func UTF8MatcherParser(l log.Logger) ParseMatcher { // UTF8MatchersParser uses the new matcher/parse parser to parse zero or more // matchers in the input string. If this fails it does not revert to the -// pkg/labels parser. +// matchers/oldparse parser. func UTF8MatchersParser(l log.Logger) ParseMatchers { - return func(input, origin string) (matchers labels.Matchers, err error) { + return func(input, origin string) (matchers matcher.Matchers, err error) { level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser", "input", input, "origin", origin) return parse.Matchers(input) } } // FallbackMatcherParser uses the new matcher/parse parser to parse zero or more -// matchers in the string. If this fails it reverts to the pkg/labels parser and -// emits a warning log line. +// matchers in the string. If this fails it reverts to the matchers/oldparse +// parser and emits a warning log line. func FallbackMatcherParser(l log.Logger) ParseMatcher { - return func(input, origin string) (matcher *labels.Matcher, err error) { + return func(input, origin string) (matcher *matcher.Matcher, err error) { level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) if strings.HasPrefix(input, "{") || strings.HasSuffix(input, "}") { return nil, fmt.Errorf("unexpected open or close brace: %s", input) @@ -124,14 +126,14 @@ func FallbackMatcherParser(l log.Logger) ParseMatcher { // Parse the input in both parsers to look for disagreement and incompatible // inputs. nMatcher, nErr := parse.Matcher(input) - cMatcher, cErr := labels.ParseMatcher(input) + cMatcher, cErr := oldparse.ParseMatcher(input) if nErr != nil { // If the input is invalid in both parsers, return the error. if cErr != nil { return nil, cErr } - // The input is valid in the pkg/labels parser, but not the matcher/parse - // parser. This means the input is not forwards compatible. + // The input is valid in the matchers/oldparse parser, but not the + // matcher/parse parser. This means the input is not forwards compatible. suggestion := cMatcher.String() level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatcher, nil @@ -147,22 +149,22 @@ func FallbackMatcherParser(l log.Logger) ParseMatcher { } // FallbackMatchersParser uses the new matcher/parse parser to parse the -// matcher in the input string. If this fails it falls back to the pkg/labels -// parser and emits a warning log line. +// matcher in the input string. If this fails it falls back to the +// matchers/oldparse parser and emits a warning log line. func FallbackMatchersParser(l log.Logger) ParseMatchers { - return func(input, origin string) (matchers labels.Matchers, err error) { + return func(input, origin string) (matchers matcher.Matchers, err error) { level.Debug(l).Log("msg", "Parsing with UTF-8 matchers parser, with fallback to classic matchers parser", "input", input, "origin", origin) // Parse the input in both parsers to look for disagreement and incompatible // inputs. nMatchers, nErr := parse.Matchers(input) - cMatchers, cErr := labels.ParseMatchers(input) + cMatchers, cErr := oldparse.ParseMatchers(input) if nErr != nil { // If the input is invalid in both parsers, return the error. if cErr != nil { return nil, cErr } - // The input is valid in the pkg/labels parser, but not the matcher/parse - // parser. This means the input is not forwards compatible. + // The input is valid in the matchers/oldparse parser, but not the + // matcher/parse parser. This means the input is not forwards compatible. var sb strings.Builder for i, n := range cMatchers { sb.WriteString(n.String()) @@ -171,15 +173,15 @@ func FallbackMatchersParser(l log.Logger) ParseMatchers { } } suggestion := sb.String() - // The input is valid in the pkg/labels parser, but not the + // The input is valid in the matchers/oldparse parser, but not the // new matcher/parse parser. level.Warn(l).Log("msg", "Alertmanager is moving to a new parser for labels and matchers, and this input is incompatible. Alertmanager has instead parsed the input using the classic matchers parser as a fallback. To make this input compatible with the UTF-8 matchers parser please make sure all regular expressions and values are double-quoted. If you are still seeing this message please open an issue.", "input", input, "origin", origin, "err", nErr, "suggestion", suggestion) return cMatchers, nil } // If the input is valid in both parsers, but produces different results, - // then there is disagreement. We need to compare to labels.Matchers(cMatchers) - // as cMatchers is a []*labels.Matcher not labels.Matchers. - if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatchers, labels.Matchers(cMatchers)) { + // then there is disagreement. We need to compare to matcher.Matchers(cMatchers) + // as cMatchers is a []*matcher.Matcher not matcher.Matchers. + if nErr == nil && cErr == nil && !reflect.DeepEqual(nMatchers, matcher.Matchers(cMatchers)) { level.Warn(l).Log("msg", "Matchers input has disagreement", "input", input, "origin", origin) return cMatchers, nil } diff --git a/matcher/compat/parse_test.go b/matcher/compat/parse_test.go index bb417ffbae..944c64e75f 100644 --- a/matcher/compat/parse_test.go +++ b/matcher/compat/parse_test.go @@ -20,19 +20,19 @@ import ( "github.com/prometheus/common/model" "github.com/stretchr/testify/require" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) func TestFallbackMatcherParser(t *testing.T) { tests := []struct { name string input string - expected *labels.Matcher + expected *matcher.Matcher err string }{{ name: "input is accepted", input: "foo=bar", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "input is accepted in neither", input: "foo!bar", @@ -40,18 +40,18 @@ func TestFallbackMatcherParser(t *testing.T) { }, { name: "input is accepted in matchers/parse but not pkg/labels", input: "foo๐Ÿ™‚=bar", - expected: mustNewMatcher(t, labels.MatchEqual, "foo๐Ÿ™‚", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo๐Ÿ™‚", "bar"), }, { name: "input is accepted in pkg/labels but not matchers/parse", input: "foo=!bar\\n", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "!bar\n"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "!bar\n"), }, { // This input causes disagreement because \xf0\x9f\x99\x82 is the byte sequence for ๐Ÿ™‚, // which is not understood by pkg/labels but is understood by matchers/parse. In such cases, - // the fallback parser returns the result from pkg/labels. + // the fallback parser returns the result from pkg/matcher. name: "input causes disagreement", input: "foo=\"\\xf0\\x9f\\x99\\x82\"", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), }} for _, test := range tests { @@ -72,14 +72,14 @@ func TestFallbackMatchersParser(t *testing.T) { tests := []struct { name string input string - expected labels.Matchers + expected matcher.Matchers err string }{{ name: "input is accepted", input: "{foo=bar,bar=baz}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - mustNewMatcher(t, labels.MatchEqual, "bar", "baz"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), + mustNewMatcher(t, matcher.MatchEqual, "bar", "baz"), }, }, { name: "input is accepted in neither", @@ -88,25 +88,25 @@ func TestFallbackMatchersParser(t *testing.T) { }, { name: "input is accepted in matchers/parse but not pkg/labels", input: "{foo๐Ÿ™‚=bar,bar=baz๐Ÿ™‚}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo๐Ÿ™‚", "bar"), - mustNewMatcher(t, labels.MatchEqual, "bar", "baz๐Ÿ™‚"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo๐Ÿ™‚", "bar"), + mustNewMatcher(t, matcher.MatchEqual, "bar", "baz๐Ÿ™‚"), }, }, { name: "is accepted in pkg/labels but not matchers/parse", input: "{foo=!bar,bar=$baz\\n}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "!bar"), - mustNewMatcher(t, labels.MatchEqual, "bar", "$baz\n"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "!bar"), + mustNewMatcher(t, matcher.MatchEqual, "bar", "$baz\n"), }, }, { // This input causes disagreement because \xf0\x9f\x99\x82 is the byte sequence for ๐Ÿ™‚, // which is not understood by pkg/labels but is understood by matchers/parse. In such cases, - // the fallback parser returns the result from pkg/labels. + // the fallback parser returns the result from pkg/matcher. name: "input causes disagreement", input: "{foo=\"\\xf0\\x9f\\x99\\x82\"}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "\\xf0\\x9f\\x99\\x82"), }, }} @@ -124,8 +124,8 @@ func TestFallbackMatchersParser(t *testing.T) { } } -func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labels.Matcher { - m, err := labels.NewMatcher(op, name, value) +func mustNewMatcher(t *testing.T, op matcher.MatchType, name, value string) *matcher.Matcher { + m, err := matcher.NewMatcher(op, name, value) require.NoError(t, err) return m } diff --git a/matcher/compliance/compliance_test.go b/matcher/compliance/compliance_test.go index 2fd39ec68d..2bb62454fa 100644 --- a/matcher/compliance/compliance_test.go +++ b/matcher/compliance/compliance_test.go @@ -17,352 +17,352 @@ import ( "reflect" "testing" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/parse" - "github.com/prometheus/alertmanager/pkg/labels" ) func TestCompliance(t *testing.T) { for _, tc := range []struct { input string - want labels.Matchers + want matcher.Matchers err string skip bool }{ { input: `{}`, - want: labels.Matchers{}, + want: matcher.Matchers{}, skip: true, }, { input: `{foo='}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "'") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "'") return append(ms, m) }(), skip: true, }, { input: "{foo=`}", - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "`") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "`") return append(ms, m) }(), skip: true, }, { input: `{foo=\n}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\n") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "\n") return append(ms, m) }(), skip: true, }, { input: `{foo=bar\n}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\n") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\n") return append(ms, m) }(), skip: true, }, { input: `{foo=\t}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\\t") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "\\t") return append(ms, m) }(), skip: true, }, { input: `{foo=bar\t}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\\t") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\\t") return append(ms, m) }(), skip: true, }, { input: `{foo=bar\}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar\\") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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\\") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\\") return append(ms, m) }(), skip: true, }, { input: `{foo=\"}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "\"") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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\"") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") return append(ms, m) }(), }, { input: `{foo="bar"}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "bar") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") return append(ms, m) }(), }, { input: `{foo=~bar.*}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchRegexp, "foo", "bar.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "foo", "bar.*") return append(ms, m) }(), }, { input: `{foo=~"bar.*"}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchRegexp, "foo", "bar.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "foo", "bar.*") return append(ms, m) }(), }, { input: `{foo!=bar}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchNotEqual, "foo", "bar") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "foo", "bar") return append(ms, m) }(), }, { input: `{foo!="bar"}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchNotEqual, "foo", "bar") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "foo", "bar") return append(ms, m) }(), }, { input: `{foo!~bar.*}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchNotRegexp, "foo", "bar.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "foo", "bar.*") return append(ms, m) }(), }, { input: `{foo!~"bar.*"}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchNotRegexp, "foo", "bar.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.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.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "baz", ".*quux") + m3, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotEqual, "baz", "quux") + m3, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "baz", ".*quux.*") + m3, _ := matcher.NewMatcher(matcher.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.*") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchRegexp, "instance", "some-api.*") return append(ms, m, m2) }(), }, { input: `{foo=""}`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar,quux") + m2, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.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โ€ฆ"`) + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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..") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "statuscode", "5..") return append(ms, m) }(), }, { input: `tricky=~~~`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchRegexp, "tricky", "~~") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "tricky", "~~") return append(ms, m) }(), skip: true, }, { input: `trickier==\\=\=\"`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "trickier", `=\=\="`) + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "contains_quote", `"`) + m2, _ := matcher.NewMatcher(matcher.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}") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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}}") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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", "}") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m1, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "") + m2, _ := matcher.NewMatcher(matcher.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} + want: func() matcher.Matchers { + m, _ := matcher.NewMatcher(matcher.MatchEqual, "job", "") + return matcher.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} + want: func() matcher.Matchers { + m, _ := matcher.NewMatcher(matcher.MatchEqual, "name-with-dashes", "bar") + return matcher.Matchers{m} }(), }, { @@ -420,18 +420,18 @@ func TestCompliance(t *testing.T) { }, { input: `{foo=`, - want: func() labels.Matchers { - ms := labels.Matchers{} - m, _ := labels.NewMatcher(labels.MatchEqual, "foo", "") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.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") + want: func() matcher.Matchers { + ms := matcher.Matchers{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "}b") return append(ms, m) }(), skip: true, diff --git a/pkg/labels/matcher.go b/matcher/matcher.go similarity index 99% rename from pkg/labels/matcher.go rename to matcher/matcher.go index eba6b4ca60..c77d2c7956 100644 --- a/pkg/labels/matcher.go +++ b/matcher/matcher.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package labels +package matcher import ( "bytes" diff --git a/pkg/labels/matcher_test.go b/matcher/matcher_test.go similarity index 99% rename from pkg/labels/matcher_test.go rename to matcher/matcher_test.go index 55d202f5a2..73e0579ce1 100644 --- a/pkg/labels/matcher_test.go +++ b/matcher/matcher_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package labels +package matcher import ( "encoding/json" diff --git a/pkg/labels/parse.go b/matcher/oldparse/parse.go similarity index 91% rename from pkg/labels/parse.go rename to matcher/oldparse/parse.go index f84b29e84b..6f05fa8d1d 100644 --- a/pkg/labels/parse.go +++ b/matcher/oldparse/parse.go @@ -11,24 +11,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package labels +package oldparse import ( "fmt" "regexp" "strings" "unicode/utf8" + + "github.com/prometheus/alertmanager/matcher" ) var ( // '=~' has to come before '=' because otherwise only the '=' // will be consumed, and the '~' will be part of the 3rd token. re = regexp.MustCompile(`^\s*([a-zA-Z_:][a-zA-Z0-9_:]*)\s*(=~|=|!=|!~)\s*((?s).*?)\s*$`) - typeMap = map[string]MatchType{ - "=": MatchEqual, - "!=": MatchNotEqual, - "=~": MatchRegexp, - "!~": MatchNotRegexp, + typeMap = map[string]matcher.MatchType{ + "=": matcher.MatchEqual, + "!=": matcher.MatchNotEqual, + "=~": matcher.MatchRegexp, + "!~": matcher.MatchNotRegexp, } ) @@ -52,8 +54,8 @@ var ( // statuscode=~"5.." // // See ParseMatcher for details on how an individual Matcher is parsed. -func ParseMatchers(s string) ([]*Matcher, error) { - matchers := []*Matcher{} +func ParseMatchers(s string) ([]*matcher.Matcher, error) { + matchers := []*matcher.Matcher{} s = strings.TrimPrefix(s, "{") s = strings.TrimSuffix(s, "}") @@ -114,7 +116,7 @@ func ParseMatchers(s string) ([]*Matcher, error) { // character). However, literal line feed characters are tolerated, as are // single '\' characters not followed by '\', 'n', or '"'. They act as a literal // backslash in that case. -func ParseMatcher(s string) (_ *Matcher, err error) { +func ParseMatcher(s string) (_ *matcher.Matcher, err error) { ms := re.FindStringSubmatch(s) if len(ms) == 0 { return nil, fmt.Errorf("bad matcher format: %s", s) @@ -174,5 +176,5 @@ func ParseMatcher(s string) (_ *Matcher, err error) { return nil, fmt.Errorf("matcher value contains unescaped double quote: %s", ms[3]) } - return NewMatcher(typeMap[ms[2]], ms[1], value.String()) + return matcher.NewMatcher(typeMap[ms[2]], ms[1], value.String()) } diff --git a/matcher/oldparse/parse_test.go b/matcher/oldparse/parse_test.go new file mode 100644 index 0000000000..31de4a7dcf --- /dev/null +++ b/matcher/oldparse/parse_test.go @@ -0,0 +1,420 @@ +// Copyright 2018 Prometheus Team +// 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 oldparse + +import ( + "reflect" + "testing" + + "github.com/prometheus/alertmanager/matcher" +) + +func TestMatchers(t *testing.T) { + for _, tc := range []struct { + input string + want []*matcher.Matcher + err string + }{ + { + input: `{}`, + want: make([]*matcher.Matcher, 0), + }, + { + input: `,`, + err: "bad matcher format: ", + }, + { + input: `{,}`, + err: "bad matcher format: ", + }, + { + input: `{foo='}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "'") + return append(ms, m) + }(), + }, + { + input: "{foo=`}", + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "`") + return append(ms, m) + }(), + }, + { + input: "{foo=\\\"}", + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "\"") + return append(ms, m) + }(), + }, + { + input: `{foo=bar}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + return append(ms, m) + }(), + }, + { + input: `{foo="bar"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + return append(ms, m) + }(), + }, + { + input: `{foo=~bar.*}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "foo", "bar.*") + return append(ms, m) + }(), + }, + { + input: `{foo=~"bar.*"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "foo", "bar.*") + return append(ms, m) + }(), + }, + { + input: `{foo!=bar}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "foo", "bar") + return append(ms, m) + }(), + }, + { + input: `{foo!="bar"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "foo", "bar") + return append(ms, m) + }(), + }, + { + input: `{foo!~bar.*}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "foo", "bar.*") + return append(ms, m) + }(), + }, + { + input: `{foo!~"bar.*"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "foo", "bar.*") + return append(ms, m) + }(), + }, + { + input: `{foo="bar", baz!="quux"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotEqual, "baz", "quux") + return append(ms, m, m2) + }(), + }, + { + input: `{foo="bar", baz!~"quux.*"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "baz", "quux.*") + return append(ms, m, m2) + }(), + }, + { + input: `{foo="bar",baz!~".*quux", derp="wat"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "baz", ".*quux") + m3, _ := matcher.NewMatcher(matcher.MatchEqual, "derp", "wat") + return append(ms, m, m2, m3) + }(), + }, + { + input: `{foo="bar", baz!="quux", derp="wat"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotEqual, "baz", "quux") + m3, _ := matcher.NewMatcher(matcher.MatchEqual, "derp", "wat") + return append(ms, m, m2, m3) + }(), + }, + { + input: `{foo="bar", baz!~".*quux.*", derp="wat"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "baz", ".*quux.*") + m3, _ := matcher.NewMatcher(matcher.MatchEqual, "derp", "wat") + return append(ms, m, m2, m3) + }(), + }, + { + input: `{foo="bar", instance=~"some-api.*"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchRegexp, "instance", "some-api.*") + return append(ms, m, m2) + }(), + }, + { + input: `{foo=""}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "") + return append(ms, m) + }(), + }, + { + input: `{foo="bar,quux", job="job1"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar,quux") + m2, _ := matcher.NewMatcher(matcher.MatchEqual, "job", "job1") + return append(ms, m, m2) + }(), + }, + { + input: `{foo = "bar", dings != "bums", }`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotEqual, "dings", "bums") + return append(ms, m, m2) + }(), + }, + { + input: `foo=bar,dings!=bums`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar") + m2, _ := matcher.NewMatcher(matcher.MatchNotEqual, "dings", "bums") + return append(ms, m, m2) + }(), + }, + { + input: `{quote="She said: \"Hi, ladies! That's gender-neutralโ€ฆ\""}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "quote", `She said: "Hi, ladies! That's gender-neutralโ€ฆ"`) + return append(ms, m) + }(), + }, + { + input: `statuscode=~"5.."`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "statuscode", "5..") + return append(ms, m) + }(), + }, + { + input: `tricky=~~~`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchRegexp, "tricky", "~~") + return append(ms, m) + }(), + }, + { + input: `trickier==\\=\=\"`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "trickier", `=\=\="`) + return append(ms, m) + }(), + }, + { + input: `contains_quote != "\"" , contains_comma !~ "foo,bar" , `, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchNotEqual, "contains_quote", `"`) + m2, _ := matcher.NewMatcher(matcher.MatchNotRegexp, "contains_comma", "foo,bar") + return append(ms, m, m2) + }(), + }, + { + input: `{foo=bar}}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar}") + return append(ms, m) + }(), + }, + { + input: `{foo=bar}},}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar}}") + return append(ms, m) + }(), + }, + { + input: `{foo=,bar=}}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m1, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "") + m2, _ := matcher.NewMatcher(matcher.MatchEqual, "bar", "}") + return append(ms, m1, m2) + }(), + }, + { + input: `{foo=bar\t}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\\t") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\n}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\n") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\\}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\\") + return append(ms, m) + }(), + }, + { + input: `{foo=bar\"}`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "bar\"") + return append(ms, m) + }(), + }, + { + input: `job=`, + want: func() []*matcher.Matcher { + m, _ := matcher.NewMatcher(matcher.MatchEqual, "job", "") + return []*matcher.Matcher{m} + }(), + }, + { + 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: `{invalid-name = "valid label"}`, + err: `bad matcher format: invalid-name = "valid label"`, + }, + { + 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() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "") + return append(ms, m) + }(), + }, + { + input: `{foo=}b`, + want: func() []*matcher.Matcher { + ms := []*matcher.Matcher{} + m, _ := matcher.NewMatcher(matcher.MatchEqual, "foo", "}b") + return append(ms, m) + }(), + }, + } { + t.Run(tc.input, func(t *testing.T) { + got, err := ParseMatchers(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 err != nil && err.Error() != tc.err { + t.Fatalf("error not equal:\ngot %v\nwant %v", err, tc.err) + } + if !reflect.DeepEqual(got, tc.want) { + t.Fatalf("labels not equal:\ngot %v\nwant %v", got, tc.want) + } + }) + } +} diff --git a/matcher/parse/parse.go b/matcher/parse/parse.go index 34e36203c0..2c13fff5aa 100644 --- a/matcher/parse/parse.go +++ b/matcher/parse/parse.go @@ -19,7 +19,7 @@ import ( "os" "runtime/debug" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) var ( @@ -37,7 +37,7 @@ var ( // Matchers parses one or more matchers in the input string. It returns an error // if the input is invalid. -func Matchers(input string) (matchers labels.Matchers, err error) { +func Matchers(input string) (matchers matcher.Matchers, err error) { defer func() { if r := recover(); r != nil { fmt.Fprintf(os.Stderr, "parser panic: %s, %s", r, debug.Stack()) @@ -50,7 +50,7 @@ func Matchers(input string) (matchers labels.Matchers, err error) { // Matcher parses the matcher in the input string. It returns an error // if the input is invalid or contains two or more matchers. -func Matcher(input string) (*labels.Matcher, error) { +func Matcher(input string) (*matcher.Matcher, error) { m, err := Matchers(input) if err != nil { return nil, err @@ -76,14 +76,14 @@ type parseFunc func(l *lexer) (parseFunc, error) // input that does not match the expected grammar, or if the tokens returned from // the lexer cannot be parsed into a complete series of matchers. type parser struct { - matchers labels.Matchers + matchers matcher.Matchers // Tracks if the input starts with an open brace and if we should expect to // parse a close brace at the end of the input. hasOpenBrace bool lexer lexer } -func (p *parser) parse() (labels.Matchers, error) { +func (p *parser) parse() (matcher.Matchers, error) { var ( err error fn = p.parseOpenBrace @@ -148,7 +148,7 @@ func (p *parser) parseMatcher(l *lexer) (parseFunc, error) { err error t token matchName, matchValue string - matchTy labels.MatchType + matchTy matcher.MatchType ) // The first token should be the label name. if t, err = p.expect(l, tokenQuoted, tokenUnquoted); err != nil { @@ -164,13 +164,13 @@ func (p *parser) parseMatcher(l *lexer) (parseFunc, error) { } switch t.kind { case tokenEquals: - matchTy = labels.MatchEqual + matchTy = matcher.MatchEqual case tokenNotEquals: - matchTy = labels.MatchNotEqual + matchTy = matcher.MatchNotEqual case tokenMatches: - matchTy = labels.MatchRegexp + matchTy = matcher.MatchRegexp case tokenNotMatches: - matchTy = labels.MatchNotRegexp + matchTy = matcher.MatchNotRegexp default: panic(fmt.Sprintf("bad operator %s", t)) } @@ -183,7 +183,7 @@ func (p *parser) parseMatcher(l *lexer) (parseFunc, error) { if err != nil { return nil, fmt.Errorf("%d:%d: %s: invalid input", t.columnStart, t.columnEnd, t.value) } - m, err := labels.NewMatcher(matchTy, matchName, matchValue) + m, err := matcher.NewMatcher(matchTy, matchName, matchValue) if err != nil { return nil, fmt.Errorf("failed to create matcher: %w", err) } diff --git a/matcher/parse/parse_test.go b/matcher/parse/parse_test.go index 1aba66853c..e189cde49e 100644 --- a/matcher/parse/parse_test.go +++ b/matcher/parse/parse_test.go @@ -18,14 +18,14 @@ import ( "github.com/stretchr/testify/require" - "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/matcher" ) func TestMatchers(t *testing.T) { tests := []struct { name string input string - expected labels.Matchers + expected matcher.Matchers error string }{{ name: "no braces", @@ -38,138 +38,138 @@ func TestMatchers(t *testing.T) { }, { name: "equals", input: "{foo=bar}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "equals with trailing comma", input: "{foo=bar,}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "not equals", input: "{foo!=bar}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar")}, }, { name: "match regex", input: "{foo=~[a-z]+}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+")}, }, { name: "doesn't match regex", input: "{foo!~[a-z]+}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+")}, }, { name: "equals unicode emoji", input: "{foo=๐Ÿ™‚}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚")}, }, { name: "equals unicode sentence", input: "{foo=๐Ÿ™‚bar}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚bar")}, }, { name: "equals without braces", input: "foo=bar", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "equals without braces but with trailing comma", input: "foo=bar,", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "not equals without braces", input: "foo!=bar", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar")}, }, { name: "match regex without braces", input: "foo=~[a-z]+", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+")}, }, { name: "doesn't match regex without braces", input: "foo!~[a-z]+", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+")}, }, { name: "equals in quotes", input: "{\"foo\"=\"bar\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "equals in quotes and with trailing comma", input: "{\"foo\"=\"bar\",}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "not equals in quotes", input: "{\"foo\"!=\"bar\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar")}, }, { name: "match regex in quotes", input: "{\"foo\"=~\"[a-z]+\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+")}, }, { name: "doesn't match regex in quotes", input: "{\"foo\"!~\"[a-z]+\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+")}, }, { name: "equals unicode emoji in quotes", input: "{\"foo\"=\"๐Ÿ™‚\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚")}, }, { name: "equals unicode emoji as bytes in quotes", input: "{\"foo\"=\"\\xf0\\x9f\\x99\\x82\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚")}, }, { name: "equals unicode emoji as code points in quotes", input: "{\"foo\"=\"\\U0001f642\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚")}, }, { name: "equals unicode sentence in quotes", input: "{\"foo\"=\"๐Ÿ™‚bar\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚bar")}, }, { name: "equals with newline in quotes", input: "{\"foo\"=\"bar\\n\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar\n")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\n")}, }, { name: "equals with tab in quotes", input: "{\"foo\"=\"bar\\t\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar\t")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\t")}, }, { name: "equals with escaped quotes in quotes", input: "{\"foo\"=\"\\\"bar\\\"\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "\"bar\"")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "\"bar\"")}, }, { name: "equals with escaped backslash in quotes", input: "{\"foo\"=\"bar\\\\\"}", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar\\")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\\")}, }, { name: "equals without braces in quotes", input: "\"foo\"=\"bar\"", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "equals without braces in quotes with trailing comma", input: "\"foo\"=\"bar\",", - expected: labels.Matchers{mustNewMatcher(t, labels.MatchEqual, "foo", "bar")}, + expected: matcher.Matchers{mustNewMatcher(t, matcher.MatchEqual, "foo", "bar")}, }, { name: "complex", input: "{foo=bar,bar!=baz}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - mustNewMatcher(t, labels.MatchNotEqual, "bar", "baz"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), + mustNewMatcher(t, matcher.MatchNotEqual, "bar", "baz"), }, }, { name: "complex in quotes", input: "{foo=\"bar\",bar!=\"baz\"}", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - mustNewMatcher(t, labels.MatchNotEqual, "bar", "baz"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), + mustNewMatcher(t, matcher.MatchNotEqual, "bar", "baz"), }, }, { name: "complex without braces", input: "foo=bar,bar!=baz", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - mustNewMatcher(t, labels.MatchNotEqual, "bar", "baz"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), + mustNewMatcher(t, matcher.MatchNotEqual, "bar", "baz"), }, }, { name: "complex without braces in quotes", input: "foo=\"bar\",bar!=\"baz\"", - expected: labels.Matchers{ - mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), - mustNewMatcher(t, labels.MatchNotEqual, "bar", "baz"), + expected: matcher.Matchers{ + mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), + mustNewMatcher(t, matcher.MatchNotEqual, "bar", "baz"), }, }, { name: "comma", @@ -230,116 +230,116 @@ func TestMatcher(t *testing.T) { tests := []struct { name string input string - expected *labels.Matcher + expected *matcher.Matcher error string }{{ name: "equals", input: "{foo=bar}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "equals with trailing comma", input: "{foo=bar,}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "not equals", input: "{foo!=bar}", - expected: mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar"), }, { name: "match regex", input: "{foo=~[a-z]+}", - expected: mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+"), }, { name: "doesn't match regex", input: "{foo!~[a-z]+}", - expected: mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+"), }, { name: "equals unicode emoji", input: "{foo=๐Ÿ™‚}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚"), }, { name: "equals unicode emoji as bytes in quotes", input: "{\"foo\"=\"\\xf0\\x9f\\x99\\x82\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚"), }, { name: "equals unicode emoji as code points in quotes", input: "{\"foo\"=\"\\U0001f642\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚"), }, { name: "equals unicode sentence", input: "{foo=๐Ÿ™‚bar}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚bar"), }, { name: "equals without braces", input: "foo=bar", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "equals without braces but with trailing comma", input: "foo=bar,", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "not equals without braces", input: "foo!=bar", - expected: mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar"), }, { name: "match regex without braces", input: "foo=~[a-z]+", - expected: mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+"), }, { name: "doesn't match regex without braces", input: "foo!~[a-z]+", - expected: mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+"), }, { name: "equals in quotes", input: "{\"foo\"=\"bar\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "equals in quotes and with trailing comma", input: "{\"foo\"=\"bar\",}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "not equals in quotes", input: "{\"foo\"!=\"bar\"}", - expected: mustNewMatcher(t, labels.MatchNotEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchNotEqual, "foo", "bar"), }, { name: "match regex in quotes", input: "{\"foo\"=~\"[a-z]+\"}", - expected: mustNewMatcher(t, labels.MatchRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchRegexp, "foo", "[a-z]+"), }, { name: "doesn't match regex in quotes", input: "{\"foo\"!~\"[a-z]+\"}", - expected: mustNewMatcher(t, labels.MatchNotRegexp, "foo", "[a-z]+"), + expected: mustNewMatcher(t, matcher.MatchNotRegexp, "foo", "[a-z]+"), }, { name: "equals unicode emoji in quotes", input: "{\"foo\"=\"๐Ÿ™‚\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚"), }, { name: "equals unicode sentence in quotes", input: "{\"foo\"=\"๐Ÿ™‚bar\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "๐Ÿ™‚bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "๐Ÿ™‚bar"), }, { name: "equals with newline in quotes", input: "{\"foo\"=\"bar\\n\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar\n"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\n"), }, { name: "equals with tab in quotes", input: "{\"foo\"=\"bar\\t\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar\t"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\t"), }, { name: "equals with escaped quotes in quotes", input: "{\"foo\"=\"\\\"bar\\\"\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "\"bar\""), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "\"bar\""), }, { name: "equals with escaped backslash in quotes", input: "{\"foo\"=\"bar\\\\\"}", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar\\"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar\\"), }, { name: "equals without braces in quotes", input: "\"foo\"=\"bar\"", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "equals without braces in quotes with trailing comma", input: "\"foo\"=\"bar\",", - expected: mustNewMatcher(t, labels.MatchEqual, "foo", "bar"), + expected: mustNewMatcher(t, matcher.MatchEqual, "foo", "bar"), }, { name: "no input", error: "no matchers", @@ -370,8 +370,8 @@ func TestMatcher(t *testing.T) { } } -func mustNewMatcher(t *testing.T, op labels.MatchType, name, value string) *labels.Matcher { - m, err := labels.NewMatcher(op, name, value) +func mustNewMatcher(t *testing.T, op matcher.MatchType, name, value string) *matcher.Matcher { + m, err := matcher.NewMatcher(op, name, value) require.NoError(t, err) return m } diff --git a/pkg/labels/parse_test.go b/pkg/labels/parse_test.go deleted file mode 100644 index dd1731c9b7..0000000000 --- a/pkg/labels/parse_test.go +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2018 Prometheus Team -// 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 labels - -import ( - "reflect" - "testing" -) - -func TestMatchers(t *testing.T) { - for _, tc := range []struct { - input string - want []*Matcher - err string - }{ - { - input: `{}`, - want: make([]*Matcher, 0), - }, - { - input: `,`, - err: "bad matcher format: ", - }, - { - input: `{,}`, - err: "bad matcher format: ", - }, - { - input: `{foo='}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "'") - return append(ms, m) - }(), - }, - { - input: "{foo=`}", - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "`") - return append(ms, m) - }(), - }, - { - input: "{foo=\\\"}", - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "\"") - return append(ms, m) - }(), - }, - { - input: `{foo=bar}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - return append(ms, m) - }(), - }, - { - input: `{foo="bar"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - return append(ms, m) - }(), - }, - { - input: `{foo=~bar.*}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchRegexp, "foo", "bar.*") - return append(ms, m) - }(), - }, - { - input: `{foo=~"bar.*"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchRegexp, "foo", "bar.*") - return append(ms, m) - }(), - }, - { - input: `{foo!=bar}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchNotEqual, "foo", "bar") - return append(ms, m) - }(), - }, - { - input: `{foo!="bar"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchNotEqual, "foo", "bar") - return append(ms, m) - }(), - }, - { - input: `{foo!~bar.*}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchNotRegexp, "foo", "bar.*") - return append(ms, m) - }(), - }, - { - input: `{foo!~"bar.*"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchNotRegexp, "foo", "bar.*") - return append(ms, m) - }(), - }, - { - input: `{foo="bar", baz!="quux"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotEqual, "baz", "quux") - return append(ms, m, m2) - }(), - }, - { - input: `{foo="bar", baz!~"quux.*"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotRegexp, "baz", "quux.*") - return append(ms, m, m2) - }(), - }, - { - input: `{foo="bar",baz!~".*quux", derp="wat"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotRegexp, "baz", ".*quux") - m3, _ := NewMatcher(MatchEqual, "derp", "wat") - return append(ms, m, m2, m3) - }(), - }, - { - input: `{foo="bar", baz!="quux", derp="wat"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotEqual, "baz", "quux") - m3, _ := NewMatcher(MatchEqual, "derp", "wat") - return append(ms, m, m2, m3) - }(), - }, - { - input: `{foo="bar", baz!~".*quux.*", derp="wat"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotRegexp, "baz", ".*quux.*") - m3, _ := NewMatcher(MatchEqual, "derp", "wat") - return append(ms, m, m2, m3) - }(), - }, - { - input: `{foo="bar", instance=~"some-api.*"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchRegexp, "instance", "some-api.*") - return append(ms, m, m2) - }(), - }, - { - input: `{foo=""}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "") - return append(ms, m) - }(), - }, - { - input: `{foo="bar,quux", job="job1"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar,quux") - m2, _ := NewMatcher(MatchEqual, "job", "job1") - return append(ms, m, m2) - }(), - }, - { - input: `{foo = "bar", dings != "bums", }`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotEqual, "dings", "bums") - return append(ms, m, m2) - }(), - }, - { - input: `foo=bar,dings!=bums`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar") - m2, _ := NewMatcher(MatchNotEqual, "dings", "bums") - return append(ms, m, m2) - }(), - }, - { - input: `{quote="She said: \"Hi, ladies! That's gender-neutralโ€ฆ\""}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "quote", `She said: "Hi, ladies! That's gender-neutralโ€ฆ"`) - return append(ms, m) - }(), - }, - { - input: `statuscode=~"5.."`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchRegexp, "statuscode", "5..") - return append(ms, m) - }(), - }, - { - input: `tricky=~~~`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchRegexp, "tricky", "~~") - return append(ms, m) - }(), - }, - { - input: `trickier==\\=\=\"`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "trickier", `=\=\="`) - return append(ms, m) - }(), - }, - { - input: `contains_quote != "\"" , contains_comma !~ "foo,bar" , `, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchNotEqual, "contains_quote", `"`) - m2, _ := NewMatcher(MatchNotRegexp, "contains_comma", "foo,bar") - return append(ms, m, m2) - }(), - }, - { - input: `{foo=bar}}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar}") - return append(ms, m) - }(), - }, - { - input: `{foo=bar}},}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar}}") - return append(ms, m) - }(), - }, - { - input: `{foo=,bar=}}`, - want: func() []*Matcher { - ms := []*Matcher{} - m1, _ := NewMatcher(MatchEqual, "foo", "") - m2, _ := NewMatcher(MatchEqual, "bar", "}") - return append(ms, m1, m2) - }(), - }, - { - input: `{foo=bar\t}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar\\t") - return append(ms, m) - }(), - }, - { - input: `{foo=bar\n}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar\n") - return append(ms, m) - }(), - }, - { - input: `{foo=bar\}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar\\") - return append(ms, m) - }(), - }, - { - input: `{foo=bar\\}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar\\") - return append(ms, m) - }(), - }, - { - input: `{foo=bar\"}`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "bar\"") - return append(ms, m) - }(), - }, - { - input: `job=`, - want: func() []*Matcher { - m, _ := NewMatcher(MatchEqual, "job", "") - return []*Matcher{m} - }(), - }, - { - 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: `{invalid-name = "valid label"}`, - err: `bad matcher format: invalid-name = "valid label"`, - }, - { - 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() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "") - return append(ms, m) - }(), - }, - { - input: `{foo=}b`, - want: func() []*Matcher { - ms := []*Matcher{} - m, _ := NewMatcher(MatchEqual, "foo", "}b") - return append(ms, m) - }(), - }, - } { - t.Run(tc.input, func(t *testing.T) { - got, err := ParseMatchers(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 err != nil && err.Error() != tc.err { - t.Fatalf("error not equal:\ngot %v\nwant %v", err, tc.err) - } - if !reflect.DeepEqual(got, tc.want) { - t.Fatalf("labels not equal:\ngot %v\nwant %v", got, tc.want) - } - }) - } -} diff --git a/silence/silence.go b/silence/silence.go index 261904cd39..68cfa4e4da 100644 --- a/silence/silence.go +++ b/silence/silence.go @@ -37,8 +37,8 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/alertmanager/cluster" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" pb "github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/types" ) @@ -49,12 +49,12 @@ var ErrNotFound = errors.New("silence not found") // ErrInvalidState is returned if the state isn't valid. var ErrInvalidState = errors.New("invalid state") -type matcherCache map[string]labels.Matchers +type matcherCache map[string]matcher.Matchers // Get retrieves the matchers for a given silence. If it is a missed cache // access, it compiles and adds the matchers of the requested silence to the // cache. -func (c matcherCache) Get(s *pb.Silence) (labels.Matchers, error) { +func (c matcherCache) Get(s *pb.Silence) (matcher.Matchers, error) { if m, ok := c[s.Id]; ok { return m, nil } @@ -63,24 +63,24 @@ func (c matcherCache) Get(s *pb.Silence) (labels.Matchers, error) { // add compiles a silences' matchers and adds them to the cache. // It returns the compiled matchers. -func (c matcherCache) add(s *pb.Silence) (labels.Matchers, error) { - ms := make(labels.Matchers, len(s.Matchers)) +func (c matcherCache) add(s *pb.Silence) (matcher.Matchers, error) { + ms := make(matcher.Matchers, len(s.Matchers)) for i, m := range s.Matchers { - var mt labels.MatchType + var mt matcher.MatchType switch m.Type { case pb.Matcher_EQUAL: - mt = labels.MatchEqual + mt = matcher.MatchEqual case pb.Matcher_NOT_EQUAL: - mt = labels.MatchNotEqual + mt = matcher.MatchNotEqual case pb.Matcher_REGEXP: - mt = labels.MatchRegexp + mt = matcher.MatchRegexp case pb.Matcher_NOT_REGEXP: - mt = labels.MatchNotRegexp + mt = matcher.MatchNotRegexp default: return nil, fmt.Errorf("unknown matcher type %q", m.Type) } - matcher, err := labels.NewMatcher(mt, m.Name, m.Pattern) + matcher, err := matcher.NewMatcher(mt, m.Name, m.Pattern) if err != nil { return nil, err } diff --git a/types/types.go b/types/types.go index 727ac320e3..893b57d06a 100644 --- a/types/types.go +++ b/types/types.go @@ -22,8 +22,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" + "github.com/prometheus/alertmanager/matcher" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/pkg/labels" ) // AlertState is used as part of AlertStatus. @@ -493,7 +493,7 @@ type Silence struct { ID string `json:"id"` // A set of matchers determining if a label set is affected // by the silence. - Matchers labels.Matchers `json:"matchers"` + Matchers matcher.Matchers `json:"matchers"` // Time range of the silence. //