From b1ce7e8a8370e07aecb4b10c0b1810db888c3d8a Mon Sep 17 00:00:00 2001 From: Nihal <38865967+wasim-nihal@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:51:37 +0530 Subject: [PATCH] Alerting: Reduce cyclomatic complexity of PrepareRuleGroupStatuses (#89649) * reduce cyclomatic complexity of PrepareRuleGroupStatuses. see https://github.com/grafana/grafana/issues/88670 Signed-off-by: Syed Nihal * reduce cyclomatic complexity for PrepareRuleGroupStatuses in pkg/services/ngalert/api/api_prometheus.go Signed-off-by: Syed Nihal * reduce cyclomatic complexity for PrepareRuleGroupStatuses in pkg/services/ngalert/api/api_prometheus.go Signed-off-by: Syed Nihal --------- Signed-off-by: Syed Nihal --- pkg/services/ngalert/api/api_prometheus.go | 96 +++++++++++----------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/pkg/services/ngalert/api/api_prometheus.go b/pkg/services/ngalert/api/api_prometheus.go index d5c6343f780..7953772e5b1 100644 --- a/pkg/services/ngalert/api/api_prometheus.go +++ b/pkg/services/ngalert/api/api_prometheus.go @@ -235,9 +235,6 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon return response.JSON(ruleResponse.HTTPStatusCode(), ruleResponse) } -// TODO: Refactor this function to reduce the cylomatic complexity -// -//nolint:gocyclo func PrepareRuleGroupStatuses(log log.Logger, manager state.AlertInstanceManager, store ListAlertRulesStore, opts RuleGroupStatusesOptions) apimodels.RuleResponse { ruleResponse := apimodels.RuleResponse{ DiscoveryBase: apimodels.DiscoveryBase{ @@ -330,28 +327,7 @@ func PrepareRuleGroupStatuses(log log.Logger, manager state.AlertInstanceManager ruleNamesSet[rn] = struct{}{} } - // Group rules together by Namespace and Rule Group. Rules are also grouped by Org ID, - // but in this API all rules belong to the same organization. Also filter by rule name if - // it was provided as a query param. - groupedRules := make(map[ngmodels.AlertRuleGroupKey][]*ngmodels.AlertRule) - for _, rule := range ruleList { - if len(ruleNamesSet) > 0 { - if _, exists := ruleNamesSet[rule.Title]; !exists { - continue - } - } - - groupKey := rule.GetGroupKey() - ruleGroup := groupedRules[groupKey] - ruleGroup = append(ruleGroup, rule) - groupedRules[groupKey] = ruleGroup - } - // Sort the rules in each rule group by index. We do this at the end instead of - // after each append to avoid having to sort each group multiple times. - for _, groupRules := range groupedRules { - ngmodels.AlertRulesBy(ngmodels.AlertRulesByIndex).Sort(groupRules) - } - + groupedRules := getGroupedRules(ruleList, ruleNamesSet) rulesTotals := make(map[string]int64, len(groupedRules)) for groupKey, rules := range groupedRules { folder, ok := opts.Namespaces[groupKey.NamespaceUID] @@ -377,27 +353,7 @@ func PrepareRuleGroupStatuses(log log.Logger, manager state.AlertInstanceManager } if len(withStates) > 0 { - // Filtering is weird but firing, pending, and normal filters also need to be - // applied to the rule. Others such as nodata and error should have no effect. - // This is to match the current behavior in the UI. - filteredRules := make([]apimodels.AlertingRule, 0, len(ruleGroup.Rules)) - for _, rule := range ruleGroup.Rules { - var state *eval.State - switch rule.State { - case "normal", "inactive": - state = util.Pointer(eval.Normal) - case "alerting", "firing": - state = util.Pointer(eval.Alerting) - case "pending": - state = util.Pointer(eval.Pending) - } - if state != nil { - if _, ok := withStatesFast[*state]; ok { - filteredRules = append(filteredRules, rule) - } - } - } - ruleGroup.Rules = filteredRules + filterRules(ruleGroup, withStatesFast) } if limitRulesPerGroup > -1 && int64(len(ruleGroup.Rules)) > limitRulesPerGroup { @@ -418,6 +374,54 @@ func PrepareRuleGroupStatuses(log log.Logger, manager state.AlertInstanceManager return ruleResponse } +func getGroupedRules(ruleList ngmodels.RulesGroup, ruleNamesSet map[string]struct{}) map[ngmodels.AlertRuleGroupKey][]*ngmodels.AlertRule { + // Group rules together by Namespace and Rule Group. Rules are also grouped by Org ID, + // but in this API all rules belong to the same organization. Also filter by rule name if + // it was provided as a query param. + groupedRules := make(map[ngmodels.AlertRuleGroupKey][]*ngmodels.AlertRule) + for _, rule := range ruleList { + if len(ruleNamesSet) > 0 { + if _, exists := ruleNamesSet[rule.Title]; !exists { + continue + } + } + groupKey := rule.GetGroupKey() + ruleGroup := groupedRules[groupKey] + ruleGroup = append(ruleGroup, rule) + groupedRules[groupKey] = ruleGroup + } + // Sort the rules in each rule group by index. We do this at the end instead of + // after each append to avoid having to sort each group multiple times. + for _, groupRules := range groupedRules { + ngmodels.AlertRulesBy(ngmodels.AlertRulesByIndex).Sort(groupRules) + } + return groupedRules +} + +func filterRules(ruleGroup *apimodels.RuleGroup, withStatesFast map[eval.State]struct{}) { + // Filtering is weird but firing, pending, and normal filters also need to be + // applied to the rule. Others such as nodata and error should have no effect. + // This is to match the current behavior in the UI. + filteredRules := make([]apimodels.AlertingRule, 0, len(ruleGroup.Rules)) + for _, rule := range ruleGroup.Rules { + var state *eval.State + switch rule.State { + case "normal", "inactive": + state = util.Pointer(eval.Normal) + case "alerting", "firing": + state = util.Pointer(eval.Alerting) + case "pending": + state = util.Pointer(eval.Pending) + } + if state != nil { + if _, ok := withStatesFast[*state]; ok { + filteredRules = append(filteredRules, rule) + } + } + } + ruleGroup.Rules = filteredRules +} + // This is the same as matchers.Matches but avoids the need to create a LabelSet func matchersMatch(matchers []*labels.Matcher, labels map[string]string) bool { for _, m := range matchers {