Alerting: Add limits to the Prometheus Rules API (#65169)
This commit adds a number of limits to the Grafana flavor of the Prometheus Rules API: 1. `limit` limits the maximum number of Rule Groups returned 2. `limit_rules` limits the maximum number of rules per Rule Group 3. `limit_alerts` limits the maximum number of alerts per rule It sorts Rule Groups and rules within Rule Groups such that data in the response is stable across requests. It also returns summaries (totals) for all Rule Groups, individual Rule Groups and rules.
This commit is contained in:
@@ -180,6 +180,37 @@ type AlertRuleWithOptionals struct {
|
||||
HasPause bool
|
||||
}
|
||||
|
||||
// AlertsRulesBy is a function that defines the ordering of alert rules.
|
||||
type AlertRulesBy func(a1, a2 *AlertRule) bool
|
||||
|
||||
func (by AlertRulesBy) Sort(rules []*AlertRule) {
|
||||
sort.Sort(AlertRulesSorter{rules: rules, by: by})
|
||||
}
|
||||
|
||||
// AlertRulesByIndex orders alert rules by rule group index. You should
|
||||
// make sure that all alert rules belong to the same rule group (have the
|
||||
// same RuleGroupKey) before using this ordering.
|
||||
func AlertRulesByIndex(a1, a2 *AlertRule) bool {
|
||||
return a1.RuleGroupIndex < a2.RuleGroupIndex
|
||||
}
|
||||
|
||||
func AlertRulesByGroupKeyAndIndex(a1, a2 *AlertRule) bool {
|
||||
k1, k2 := a1.GetGroupKey(), a2.GetGroupKey()
|
||||
if k1 == k2 {
|
||||
return a1.RuleGroupIndex < a2.RuleGroupIndex
|
||||
}
|
||||
return AlertRuleGroupKeyByNamespaceAndRuleGroup(&k1, &k2)
|
||||
}
|
||||
|
||||
type AlertRulesSorter struct {
|
||||
rules []*AlertRule
|
||||
by AlertRulesBy
|
||||
}
|
||||
|
||||
func (s AlertRulesSorter) Len() int { return len(s.rules) }
|
||||
func (s AlertRulesSorter) Swap(i, j int) { s.rules[i], s.rules[j] = s.rules[j], s.rules[i] }
|
||||
func (s AlertRulesSorter) Less(i, j int) bool { return s.by(s.rules[i], s.rules[j]) }
|
||||
|
||||
// GetDashboardUID returns the DashboardUID or "".
|
||||
func (alertRule *AlertRule) GetDashboardUID() string {
|
||||
if alertRule.DashboardUID != nil {
|
||||
@@ -303,6 +334,29 @@ func (k AlertRuleKey) String() string {
|
||||
return fmt.Sprintf("{orgID: %d, UID: %s}", k.OrgID, k.UID)
|
||||
}
|
||||
|
||||
// AlertRuleGroupKeyBy is a function that defines the ordering of alert rule group keys.
|
||||
type AlertRuleGroupKeyBy func(a1, a2 *AlertRuleGroupKey) bool
|
||||
|
||||
func (by AlertRuleGroupKeyBy) Sort(keys []AlertRuleGroupKey) {
|
||||
sort.Sort(AlertRuleGroupKeySorter{keys: keys, by: by})
|
||||
}
|
||||
|
||||
func AlertRuleGroupKeyByNamespaceAndRuleGroup(k1, k2 *AlertRuleGroupKey) bool {
|
||||
if k1.NamespaceUID == k2.NamespaceUID {
|
||||
return k1.RuleGroup < k2.RuleGroup
|
||||
}
|
||||
return k1.NamespaceUID < k2.NamespaceUID
|
||||
}
|
||||
|
||||
type AlertRuleGroupKeySorter struct {
|
||||
keys []AlertRuleGroupKey
|
||||
by AlertRuleGroupKeyBy
|
||||
}
|
||||
|
||||
func (s AlertRuleGroupKeySorter) Len() int { return len(s.keys) }
|
||||
func (s AlertRuleGroupKeySorter) Swap(i, j int) { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] }
|
||||
func (s AlertRuleGroupKeySorter) Less(i, j int) bool { return s.by(&s.keys[i], &s.keys[j]) }
|
||||
|
||||
// GetKey returns the alert definitions identifier
|
||||
func (alertRule *AlertRule) GetKey() AlertRuleKey {
|
||||
return AlertRuleKey{OrgID: alertRule.OrgID, UID: alertRule.UID}
|
||||
|
||||
@@ -17,6 +17,51 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func TestSortAlertRulesByGroupKeyAndIndex(t *testing.T) {
|
||||
tc := []struct {
|
||||
name string
|
||||
input []*AlertRule
|
||||
expected []*AlertRule
|
||||
}{{
|
||||
name: "alert rules are ordered by organization",
|
||||
input: []*AlertRule{
|
||||
{OrgID: 2, NamespaceUID: "test2"},
|
||||
{OrgID: 1, NamespaceUID: "test1"},
|
||||
},
|
||||
expected: []*AlertRule{
|
||||
{OrgID: 1, NamespaceUID: "test1"},
|
||||
{OrgID: 2, NamespaceUID: "test2"},
|
||||
},
|
||||
}, {
|
||||
name: "alert rules in same organization are ordered by namespace",
|
||||
input: []*AlertRule{
|
||||
{OrgID: 1, NamespaceUID: "test2"},
|
||||
{OrgID: 1, NamespaceUID: "test1"},
|
||||
},
|
||||
expected: []*AlertRule{
|
||||
{OrgID: 1, NamespaceUID: "test1"},
|
||||
{OrgID: 1, NamespaceUID: "test2"},
|
||||
},
|
||||
}, {
|
||||
name: "alert rules with same group key are ordered by index",
|
||||
input: []*AlertRule{
|
||||
{OrgID: 1, NamespaceUID: "test", RuleGroupIndex: 2},
|
||||
{OrgID: 1, NamespaceUID: "test", RuleGroupIndex: 1},
|
||||
},
|
||||
expected: []*AlertRule{
|
||||
{OrgID: 1, NamespaceUID: "test", RuleGroupIndex: 1},
|
||||
{OrgID: 1, NamespaceUID: "test", RuleGroupIndex: 2},
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AlertRulesBy(AlertRulesByGroupKeyAndIndex).Sort(tt.input)
|
||||
assert.EqualValues(t, tt.expected, tt.input)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoDataStateFromString(t *testing.T) {
|
||||
allKnownNoDataStates := [...]NoDataState{
|
||||
Alerting,
|
||||
|
||||
Reference in New Issue
Block a user