From 2b3742264503b1f1548e378af7b948c3f602bd15 Mon Sep 17 00:00:00 2001 From: junot <49136171+junotx@users.noreply.github.com> Date: Tue, 8 Nov 2022 11:15:26 +0800 Subject: [PATCH] add label_matcher param to filter alerts (#5353) Signed-off-by: junot Signed-off-by: junot --- pkg/api/alerting/v2beta1/types.go | 1 + pkg/kapis/alerting/v2beta1/register.go | 5 +++- pkg/models/alerting/rulegroup.go | 40 ++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/pkg/api/alerting/v2beta1/types.go b/pkg/api/alerting/v2beta1/types.go index 0cfee1787..b6e4637ea 100644 --- a/pkg/api/alerting/v2beta1/types.go +++ b/pkg/api/alerting/v2beta1/types.go @@ -35,6 +35,7 @@ const ( // for alert FieldAlertLabelFilters = "label_filters" FieldAlertActiveAt = "activeAt" + FieldAlertLabelMatcher = "label_matcher" ) var SortableFields = []string{ diff --git a/pkg/kapis/alerting/v2beta1/register.go b/pkg/kapis/alerting/v2beta1/register.go index 98788b1e3..fcf8331fe 100644 --- a/pkg/kapis/alerting/v2beta1/register.go +++ b/pkg/kapis/alerting/v2beta1/register.go @@ -66,6 +66,7 @@ func AddToContainer(container *restful.Container, informers informers.InformerFa Param(ws.QueryParameter(query.ParameterOrderBy, "sort parameters, one of `activeAt`. e.g. orderBy=activeAt")). Param(ws.QueryParameter(kapialertingv2beta1.FieldState, "state, one of `firing`, `pending`")). Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelFilters, "label filters, concatenating multiple filters with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a").DataFormat("key=%s,key~%s")). + Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelMatcher, "label matcher to match alert labels, follow prometheus matcher format. e.g. `{label_name1=\"valueA\",label_name2=~\"valueB|valueC\"}`")). Returns(http.StatusOK, kapi.StatusOK, kapi.ListResult{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AlertingTag})) @@ -96,6 +97,7 @@ func AddToContainer(container *restful.Container, informers informers.InformerFa Param(ws.QueryParameter(query.ParameterOrderBy, "sort parameters, one of `activeAt`. e.g. orderBy=activeAt")). Param(ws.QueryParameter(kapialertingv2beta1.FieldState, "state, one of `firing`, `pending`")). Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelFilters, "label filters, concatenating multiple filters with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a").DataFormat("key=%s,key~%s")). + Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelMatcher, "label matcher to match alert labels, follow prometheus matcher format. e.g. `{label_name1=\"valueA\",label_name2=~\"valueB|valueC\"}`")). Returns(http.StatusOK, kapi.StatusOK, kapi.ListResult{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AlertingTag})) @@ -120,13 +122,14 @@ func AddToContainer(container *restful.Container, informers informers.InformerFa ws.Route(ws.GET("/globalalerts"). To(handler.handleListGlobalAlerts). - Doc("list the alerts of globalrulegroups in the cluster"). + Doc("list the alerts of globalrulegroups"). Param(ws.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")). Param(ws.QueryParameter(query.ParameterLimit, "limit").Required(false)). Param(ws.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")). Param(ws.QueryParameter(query.ParameterOrderBy, "sort parameters, one of `activeAt`. e.g. orderBy=activeAt")). Param(ws.QueryParameter(kapialertingv2beta1.FieldState, "state, one of `firing`, `pending`")). Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelFilters, "label filters, concatenating multiple filters with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a").DataFormat("key=%s,key~%s")). + Param(ws.QueryParameter(kapialertingv2beta1.FieldAlertLabelMatcher, "label matcher to match alert labels, follow prometheus matcher format. e.g. `{label_name1=\"valueA\",label_name2=~\"valueB|valueC\"}`")). Param(ws.QueryParameter(kapialertingv2beta1.FieldBuiltin, "filter alerts, `true` for alerts from built-in rule groups and `false` for alerts from custom rule groups")). Returns(http.StatusOK, kapi.StatusOK, kapi.ListResult{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AlertingTag})) diff --git a/pkg/models/alerting/rulegroup.go b/pkg/models/alerting/rulegroup.go index 88c4c51c3..ff0dcc59d 100644 --- a/pkg/models/alerting/rulegroup.go +++ b/pkg/models/alerting/rulegroup.go @@ -16,9 +16,11 @@ package alerting import ( "context" + "fmt" "time" promlabels "github.com/prometheus/prometheus/pkg/labels" + "github.com/prometheus/prometheus/promql/parser" promrules "github.com/prometheus/prometheus/rules" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -243,7 +245,10 @@ func (o *ruleGroupOperator) ListAlerts(ctx context.Context, namespace string, } } - filterAlert := o.createFilterAlertFunc(queryParam) + filterAlert, err := o.createFilterAlertFunc(queryParam) + if err != nil { + return nil, err + } listResult := resources.DefaultList(alerts, queryParam, func(left, right runtime.Object, field query.Field) bool { return o.compareAlert(&left.(*wrapAlert).Alert, &right.(*wrapAlert).Alert, field) }, func(obj runtime.Object, filter query.Filter) bool { @@ -269,12 +274,20 @@ func (d *ruleGroupOperator) compareAlert(left, right *kapialertingv2beta1.Alert, return false } -func (d *ruleGroupOperator) createFilterAlertFunc(queryParam *query.Query) func(alert *kapialertingv2beta1.Alert, filter query.Filter) bool { +func (d *ruleGroupOperator) createFilterAlertFunc(queryParam *query.Query) (func(alert *kapialertingv2beta1.Alert, filter query.Filter) bool, error) { var labelFilters kapialertingv2beta1.LabelFilters + var labelMatchers []*promlabels.Matcher + var err error if len(queryParam.Filters) > 0 { if filters, ok := queryParam.Filters[kapialertingv2beta1.FieldAlertLabelFilters]; ok { labelFilters = kapialertingv2beta1.ParseLabelFilters(string(filters)) } + if matcherStr, ok := queryParam.Filters[kapialertingv2beta1.FieldAlertLabelMatcher]; ok { + labelMatchers, err = parser.ParseMetricSelector(string(matcherStr)) + if err != nil { + return nil, fmt.Errorf("invalid %s param: %v", kapialertingv2beta1.FieldAlertLabelMatcher, err) + } + } } return func(alert *kapialertingv2beta1.Alert, filter query.Filter) bool { switch filter.Field { @@ -283,11 +296,22 @@ func (d *ruleGroupOperator) createFilterAlertFunc(queryParam *query.Query) func( return true } return labelFilters.Matches(alert.Labels) + case kapialertingv2beta1.FieldAlertLabelMatcher: + for _, m := range labelMatchers { + var v string + if len(alert.Labels) > 0 { + v = alert.Labels[m.Name] + } + if !m.Matches(v) { + return false + } + } + return true case kapialertingv2beta1.FieldState: return alert.State == string(filter.Value) } return false - } + }, nil } func (o *ruleGroupOperator) GetRuleGroup(ctx context.Context, namespace, name string) (*kapialertingv2beta1.RuleGroup, error) { @@ -474,7 +498,10 @@ func (o *ruleGroupOperator) ListClusterAlerts(ctx context.Context, } } - filterAlert := o.createFilterAlertFunc(queryParam) + filterAlert, err := o.createFilterAlertFunc(queryParam) + if err != nil { + return nil, err + } listResult := resources.DefaultList(alerts, queryParam, func(left, right runtime.Object, field query.Field) bool { return o.compareAlert(&left.(*wrapAlert).Alert, &right.(*wrapAlert).Alert, field) }, func(obj runtime.Object, filter query.Filter) bool { @@ -717,7 +744,10 @@ func (o *ruleGroupOperator) ListGlobalAlerts(ctx context.Context, } } - filterAlert := o.createFilterAlertFunc(queryParam) + filterAlert, err := o.createFilterAlertFunc(queryParam) + if err != nil { + return nil, err + } listResult := resources.DefaultList(alerts, queryParam, func(left, right runtime.Object, field query.Field) bool { return o.compareAlert(&left.(*wrapAlert).Alert, &right.(*wrapAlert).Alert, field) }, func(obj runtime.Object, filter query.Filter) bool {