[v11.0.x] Alerting: Fix rule storage to filter by group names using case-sensitive comparison (#89063)

Alerting: Fix rule storage to filter by group names using case-sensitive comparison (#88992)

* add test for the bug
* remove unused struct
* update db store to post process filters by group using go-lang's case-sensitive string comparison

--------

Co-authored-by: Alexander Weaver <weaver.alex.d@gmail.com>
# Conflicts:
#	pkg/services/ngalert/store/alert_rule.go
#	pkg/services/ngalert/store/alert_rule_test.go
This commit is contained in:
Yuri Tseretyan
2024-06-11 15:59:45 -04:00
committed by GitHub
parent 0182f932e0
commit 4fbe9604ef
4 changed files with 95 additions and 13 deletions
-12
View File
@@ -630,18 +630,6 @@ type ListNamespaceAlertRulesQuery struct {
NamespaceUID string
}
// ListOrgRuleGroupsQuery is the query for listing unique rule groups
// for an organization
type ListOrgRuleGroupsQuery struct {
OrgID int64
NamespaceUIDs []string
// DashboardUID and PanelID are optional and allow filtering rules
// to return just those for a dashboard and panel.
DashboardUID string
PanelID int64
}
type UpdateRule struct {
Existing *AlertRule
New AlertRule
+34 -1
View File
@@ -113,7 +113,23 @@ func (st DBstore) GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmode
if err != nil {
return err
}
result = rules
// MySQL by default compares strings without case-sensitivity, make sure we keep the case-sensitive comparison.
var groupKey ngmodels.AlertRuleGroupKey
// find the rule, which group we fetch
for _, rule := range rules {
if rule.UID == query.UID {
groupKey = rule.GetGroupKey()
break
}
}
result = make([]*ngmodels.AlertRule, 0, len(rules))
// MySQL (and potentially other databases) can use case-insensitive comparison.
// This code makes sure we return groups that only exactly match the filter.
for _, rule := range rules {
if rule.GetGroupKey() == groupKey {
result = append(result, rule)
}
}
return nil
})
return result, err
@@ -407,6 +423,11 @@ func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertR
continue
}
}
// MySQL (and potentially other databases) can use case-insensitive comparison.
// This code makes sure we return groups that only exactly match the filter.
if query.RuleGroup != "" && query.RuleGroup != rule.RuleGroup {
continue
}
alertRules = append(alertRules, rule)
}
@@ -522,8 +543,13 @@ func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodel
alertRulesSql.NotIn("org_id", disabledOrgs)
}
var groupsMap map[string]struct{}
if len(query.RuleGroups) > 0 {
alertRulesSql.In("rule_group", query.RuleGroups)
groupsMap = make(map[string]struct{}, len(query.RuleGroups))
for _, group := range query.RuleGroups {
groupsMap[group] = struct{}{}
}
}
rule := new(ngmodels.AlertRule)
@@ -544,6 +570,13 @@ func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodel
st.Logger.Error("Invalid rule found in DB store, ignoring it", "func", "GetAlertRulesForScheduling", "error", err)
continue
}
// MySQL (and potentially other databases) uses case-insensitive comparison.
// This code makes sure we return groups that only exactly match the filter
if groupsMap != nil {
if _, ok := groupsMap[rule.RuleGroup]; !ok { // compare groups using case-sensitive logic.
continue
}
}
if st.FeatureToggles.IsEnabled(ctx, featuremgmt.FlagAlertingQueryOptimization) {
if optimizations, err := OptimizeAlertQueries(rule.Data); err != nil {
st.Logger.Error("Could not migrate rule from range to instant query", "rule", rule.UID, "err", err)
@@ -392,6 +392,11 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
ruleGroups: []string{rule1.RuleGroup},
rules: []string{rule1.Title},
},
{
name: "with a rule group filter, should be case sensitive",
ruleGroups: []string{strings.ToUpper(rule1.RuleGroup)},
rules: []string{},
},
{
name: "with a filter on orgs, it returns rules that do not belong to that org",
rules: []string{rule1.Title},