diff --git a/internal/filters/filter_age.go b/internal/filters/filter_age.go index 40d17d60a..6ee3d28ea 100644 --- a/internal/filters/filter_age.go +++ b/internal/filters/filter_age.go @@ -44,8 +44,14 @@ func (filter *ageFilter) Match(alert *models.Alert, matches int) bool { panic(e) } +func (filter *ageFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool { + ts := time.Now().Add(filter.Value.(time.Duration)) + return filter.Matcher.Compare(int(ts.Unix()), int(am.StartsAt.Unix())) +} + func newAgeFilter() FilterT { f := ageFilter{} + f.IsAlertmanagerFilter = true return &f } diff --git a/internal/filters/filter_silence_author.go b/internal/filters/filter_silence_author.go index cd5d2e93b..e3a2b7735 100644 --- a/internal/filters/filter_silence_author.go +++ b/internal/filters/filter_silence_author.go @@ -38,8 +38,23 @@ func (filter *silenceAuthorFilter) Match(alert *models.Alert, matches int) bool panic(e) } +func (filter *silenceAuthorFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool { + var isMatch bool + for _, silenceID := range am.SilencedBy { + silence, found := am.Silences[silenceID] + if found { + m := filter.Matcher.Compare(silence.CreatedBy, filter.Value) + if m { + isMatch = m + } + } + } + return isMatch +} + func newSilenceAuthorFilter() FilterT { f := silenceAuthorFilter{} + f.IsAlertmanagerFilter = true return &f } diff --git a/internal/filters/filter_silence_id.go b/internal/filters/filter_silence_id.go index 7ffe68c27..77b46602a 100644 --- a/internal/filters/filter_silence_id.go +++ b/internal/filters/filter_silence_id.go @@ -33,8 +33,20 @@ func (filter *silenceIDFilter) Match(alert *models.Alert, matches int) bool { panic(e) } +func (filter *silenceIDFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool { + var isMatch bool + for _, silenceID := range am.SilencedBy { + m := filter.Matcher.Compare(silenceID, filter.Value) + if m { + isMatch = m + } + } + return isMatch +} + func newsilenceIDFilter() FilterT { f := silenceIDFilter{} + f.IsAlertmanagerFilter = true return &f } diff --git a/internal/filters/filter_silence_ticket.go b/internal/filters/filter_silence_ticket.go index 0cfa6fa68..7444cf5e3 100644 --- a/internal/filters/filter_silence_ticket.go +++ b/internal/filters/filter_silence_ticket.go @@ -38,8 +38,23 @@ func (filter *silenceTicketFilter) Match(alert *models.Alert, matches int) bool panic(e) } +func (filter *silenceTicketFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool { + var isMatch bool + for _, silenceID := range am.SilencedBy { + silence, found := am.Silences[silenceID] + if found { + m := filter.Matcher.Compare(silence.TicketID, filter.Value) + if m { + isMatch = m + } + } + } + return isMatch +} + func newSilenceTicketFilter() FilterT { f := silenceTicketFilter{} + f.IsAlertmanagerFilter = true return &f } diff --git a/internal/filters/filter_state.go b/internal/filters/filter_state.go index 95c08a1b9..abdf249e4 100644 --- a/internal/filters/filter_state.go +++ b/internal/filters/filter_state.go @@ -37,8 +37,13 @@ func (filter *stateFilter) Match(alert *models.Alert, matches int) bool { panic(e) } +func (filter *stateFilter) MatchAlertmanager(am *models.AlertmanagerInstance) bool { + return filter.Matcher.Compare(am.State, filter.Value) +} + func newStateFilter() FilterT { f := stateFilter{} + f.IsAlertmanagerFilter = true return &f } diff --git a/internal/filters/filter_test.go b/internal/filters/filter_test.go index f52387f56..61aea954d 100644 --- a/internal/filters/filter_test.go +++ b/internal/filters/filter_test.go @@ -8,18 +8,19 @@ import ( "github.com/prymitive/karma/internal/alertmanager" "github.com/prymitive/karma/internal/filters" "github.com/prymitive/karma/internal/models" + "github.com/prymitive/karma/internal/slices" log "github.com/sirupsen/logrus" ) type filterTest struct { - Expression string - IsValid bool - IsMatch bool - IsAlertmanagerFilter bool - IsAlertmanagerMatch bool - Alert models.Alert - Silence models.Silence + Expression string + IsValid bool + IsMatch bool + IsAlertmanagerMatch bool + Alert models.Alert + Silence models.Silence + Alertmanagers []models.AlertmanagerInstance } var tests = []filterTest{ @@ -30,16 +31,18 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@state!=active", - IsValid: true, - Alert: models.Alert{}, - IsMatch: true, + Expression: "@state!=active", + IsValid: true, + Alert: models.Alert{}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@state=suppressed", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - IsMatch: true, + Expression: "@state=suppressed", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@state!=suppressed", @@ -68,10 +71,11 @@ var tests = []filterTest{ IsValid: false, }, { - Expression: "@state=suppressed", - IsValid: true, - Alert: models.Alert{State: "suppressed", InhibitedBy: []string{"999"}}, - IsMatch: true, + Expression: "@state=suppressed", + IsValid: true, + Alert: models.Alert{State: "suppressed", InhibitedBy: []string{"999"}}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@state=suppressed", @@ -141,10 +145,12 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@silence_id=abcdef", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"abcdef"}}, - IsMatch: true, + Expression: "@silence_id=abcdef", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"abcdef"}}, + Silence: models.Silence{ID: "abcdef"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_id!=abcdef", @@ -172,18 +178,20 @@ var tests = []filterTest{ }, { - Expression: "@silence_ticket=1", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1", TicketID: "1"}, - IsMatch: true, + Expression: "@silence_ticket=1", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1", TicketID: "1"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@silence_ticket=1", - IsValid: true, - Alert: models.Alert{State: "active", SilencedBy: []string{}}, - Silence: models.Silence{ID: "1", TicketID: "1"}, - IsMatch: false, + Expression: "@silence_ticket=1", + IsValid: true, + Alert: models.Alert{State: "active", SilencedBy: []string{}}, + Silence: models.Silence{ID: "1", TicketID: "1"}, + IsMatch: false, + IsAlertmanagerMatch: true, }, { Expression: "@silence_ticket=2", @@ -193,11 +201,12 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@silence_ticket!=3", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1", TicketID: "x"}, - IsMatch: true, + Expression: "@silence_ticket!=3", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1", TicketID: "x"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_ticket!=4", @@ -207,18 +216,20 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@silence_ticket!=5", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1"}, - IsMatch: true, + Expression: "@silence_ticket!=5", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@silence_ticket=~abc", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1", TicketID: "xxabcxx"}, - IsMatch: true, + Expression: "@silence_ticket=~abc", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1", TicketID: "xxabcxx"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_ticket=~abc", @@ -250,11 +261,12 @@ var tests = []filterTest{ }, { - Expression: "@silence_author=john", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1", CreatedBy: "john"}, - IsMatch: true, + Expression: "@silence_author=john", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1", CreatedBy: "john"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_author=john", @@ -271,11 +283,12 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@silence_author!=john", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1", CreatedBy: "bob"}, - IsMatch: true, + Expression: "@silence_author!=john", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1", CreatedBy: "bob"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_author!=john", @@ -285,11 +298,12 @@ var tests = []filterTest{ IsMatch: false, }, { - Expression: "@silence_author!=john", - IsValid: true, - Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, - Silence: models.Silence{ID: "1"}, - IsMatch: true, + Expression: "@silence_author!=john", + IsValid: true, + Alert: models.Alert{State: "suppressed", SilencedBy: []string{"1"}}, + Silence: models.Silence{ID: "1"}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@silence_author=~", @@ -320,10 +334,11 @@ var tests = []filterTest{ IsMatch: true, }, { - Expression: "@age>1h", - IsValid: true, - Alert: models.Alert{StartsAt: time.Now().Add(time.Hour * -2)}, - IsMatch: true, + Expression: "@age>1h", + IsValid: true, + Alert: models.Alert{StartsAt: time.Now().Add(time.Hour * -2)}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@age<-1h", @@ -332,10 +347,11 @@ var tests = []filterTest{ IsMatch: true, }, { - Expression: "@age>-1h", - IsValid: true, - Alert: models.Alert{StartsAt: time.Now().Add(time.Hour * -2)}, - IsMatch: true, + Expression: "@age>-1h", + IsValid: true, + Alert: models.Alert{StartsAt: time.Now().Add(time.Hour * -2)}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@age=1h", @@ -550,50 +566,44 @@ var tests = []filterTest{ IsValid: false, }, { - Expression: "@alertmanager=test", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: true, - IsAlertmanagerMatch: true, + Expression: "@alertmanager=test", + IsValid: true, + Alert: models.Alert{}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@alertmanager=abc", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: false, + Expression: "@alertmanager=abc", + IsValid: true, + Alert: models.Alert{}, + IsMatch: false, }, { - Expression: "@alertmanager=~tes", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: true, - IsAlertmanagerMatch: true, + Expression: "@alertmanager=~tes", + IsValid: true, + Alert: models.Alert{}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@alertmanager=~000", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: false, + Expression: "@alertmanager=~000", + IsValid: true, + Alert: models.Alert{}, + IsMatch: false, }, { - Expression: "@alertmanager!=tes", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: true, - IsAlertmanagerMatch: true, + Expression: "@alertmanager!=tes", + IsValid: true, + Alert: models.Alert{}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { - Expression: "@alertmanager!~abc", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: true, - IsAlertmanagerMatch: true, + Expression: "@alertmanager!~abc", + IsValid: true, + Alert: models.Alert{}, + IsMatch: true, + IsAlertmanagerMatch: true, }, { Expression: "@receiver=by-name", @@ -604,16 +614,14 @@ var tests = []filterTest{ IsMatch: true, }, { - Expression: "@cluster=foo", - IsAlertmanagerFilter: true, - IsValid: true, - Alert: models.Alert{}, - IsMatch: false, + Expression: "@cluster=foo", + IsValid: true, + Alert: models.Alert{}, + IsMatch: false, }, { - Expression: "@cluster=HA", - IsAlertmanagerFilter: true, - IsValid: true, + Expression: "@cluster=HA", + IsValid: true, Alert: models.Alert{ Receiver: "by-name", }, @@ -621,9 +629,8 @@ var tests = []filterTest{ IsAlertmanagerMatch: true, }, { - Expression: "@cluster!=foo", - IsAlertmanagerFilter: true, - IsValid: true, + Expression: "@cluster!=foo", + IsValid: true, Alert: models.Alert{ Receiver: "by-name", }, @@ -661,13 +668,18 @@ func TestFilters(t *testing.T) { } for _, ft := range tests { alert := models.Alert(ft.Alert) - alert.Alertmanager = []models.AlertmanagerInstance{ - { - Cluster: "HA", - Name: am.Name, - Silences: map[string]*models.Silence{}, - SilencedBy: []string{}, - }, + if len(ft.Alertmanagers) > 0 { + alert.Alertmanager = ft.Alertmanagers + } else { + alert.Alertmanager = []models.AlertmanagerInstance{ + { + Cluster: "HA", + Name: am.Name, + Silences: map[string]*models.Silence{}, + SilencedBy: []string{}, + State: ft.Alert.State, + }, + } } if ft.Silence.ID != "" { alert.Alertmanager[0].Silences[ft.Silence.ID] = &ft.Silence @@ -684,10 +696,14 @@ func TestFilters(t *testing.T) { if f.GetIsValid() != ft.IsValid { t.Errorf("[%s] GetIsValid() returned %#v while %#v was expected", ft.Expression, f.GetIsValid(), ft.IsValid) } - if f.GetIsAlertmanagerFilter() != ft.IsAlertmanagerFilter { - t.Errorf("[%s] GetIsAlertmanagerFilter() returned %#v while %#v was expected", ft.Expression, f.GetIsAlertmanagerFilter(), ft.IsAlertmanagerFilter) - } if f.GetIsValid() { + isAlertmanagerFilter := slices.StringInSlice( + []string{"@age", "@alertmanager", "@cluster", "@state", "@silence_id", "@silence_ticket", "@silence_author"}, + f.GetName()) + if isAlertmanagerFilter != f.GetIsAlertmanagerFilter() { + t.Errorf("[%s] GetIsAlertmanagerFilter() returned %#v while %#v was expected", ft.Expression, f.GetIsAlertmanagerFilter(), isAlertmanagerFilter) + } + m := f.Match(&alert, 0) if m != ft.IsMatch { j, _ := json.Marshal(ft.Alert) @@ -704,7 +720,7 @@ func TestFilters(t *testing.T) { t.Errorf("[%s] GetRawText() returned %#v != %s passed as the expression", ft.Expression, f.GetRawText(), ft.Expression) } - if f.GetIsAlertmanagerFilter() { + if m && f.GetIsAlertmanagerFilter() { for _, am := range alert.Alertmanager { m := f.MatchAlertmanager(&am) if m != ft.IsAlertmanagerMatch {