diff --git a/api/v2/api.go b/api/v2/api.go index 24f308d5..3394206e 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -584,14 +584,14 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew sils := open_api_models.GettableSilences{} for _, ps := range psils { + if !checkSilenceMatchesFilterLabels(ps, matchers) { + continue + } silence, err := gettableSilenceFromProto(ps) if err != nil { level.Error(logger).Log("msg", "Failed to unmarshal silence from proto", "err", err) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) } - if !gettableSilenceMatchesFilterLabels(silence, matchers) { - continue - } sils = append(sils, &silence) } @@ -638,13 +638,31 @@ func sortSilences(sils open_api_models.GettableSilences) { }) } -func gettableSilenceMatchesFilterLabels(s open_api_models.GettableSilence, matchers []*labels.Matcher) bool { - sms := make(map[string]string) - for _, m := range s.Matchers { - sms[*m.Name] = *m.Value +// checkSilenceMatchesFilterLabels returns true if +// a given silence matches a list of matchers. +// 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 { + 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 { + found = true + break + } + } + if !found { + return false + } } - return matchFilterLabels(matchers, sms) + return true } func (api *API) getSilenceHandler(params silence_ops.GetSilenceParams) middleware.Responder { diff --git a/api/v2/api_test.go b/api/v2/api_test.go index ca55c59c..7654687c 100644 --- a/api/v2/api_test.go +++ b/api/v2/api_test.go @@ -26,6 +26,7 @@ import ( general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/pkg/labels" + "github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/types" ) @@ -130,6 +131,109 @@ func TestGetSilencesHandler(t *testing.T) { } } +func createSilenceMatcher(name string, pattern string, matcherType silencepb.Matcher_Type) *silencepb.Matcher { + return &silencepb.Matcher{ + Name: name, + Pattern: pattern, + Type: matcherType, + } +} + +func createLabelMatcher(name string, value string, matchType labels.MatchType) *labels.Matcher { + matcher, _ := labels.NewMatcher(matchType, name, value) + return matcher +} + +func TestCheckSilenceMatchesFilterLabels(t *testing.T) { + type test struct { + silenceMatchers []*silencepb.Matcher + filterMatchers []*labels.Matcher + expected bool + } + + tests := []test{ + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchEqual)}, + true, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "novalue", labels.MatchEqual)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "(foo|bar)", silencepb.Matcher_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)}, + true, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "foo", silencepb.Matcher_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)}, + false, + }, + + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchRegexp)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchEqual)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)}, + true, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)}, + true, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_EQUAL)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotRegexp)}, + false, + }, + { + []*silencepb.Matcher{createSilenceMatcher("label", "value", silencepb.Matcher_NOT_REGEXP)}, + []*labels.Matcher{createLabelMatcher("label", "value", labels.MatchNotEqual)}, + false, + }, + { + []*silencepb.Matcher{ + createSilenceMatcher("label", "(foo|bar)", silencepb.Matcher_REGEXP), + createSilenceMatcher("label", "value", silencepb.Matcher_EQUAL), + }, + []*labels.Matcher{createLabelMatcher("label", "(foo|bar)", labels.MatchRegexp)}, + true, + }, + } + + for _, test := range tests { + silence := silencepb.Silence{ + Matchers: test.silenceMatchers, + } + actual := checkSilenceMatchesFilterLabels(&silence, test.filterMatchers) + if test.expected != actual { + t.Fatal("unexpected match result between silence and filter. expected:", test.expected, ", actual:", actual) + } + } +} + func convertDateTime(ts time.Time) *strfmt.DateTime { dt := strfmt.DateTime(ts) return &dt