Alerting: Support for simplified notification settings in rule API (#81011)
* Add notification settings to storage\domain and API models. Settings are a slice to workaround XORM mapping * Support validation of notification settings when rules are updated * Implement route generator for Alertmanager configuration. That fetches all notification settings. * Update multi-tenant Alertmanager to run the generator before applying the configuration. * Add notification settings labels to state calculation * update the Multi-tenant Alertmanager to provide validation for notification settings * update GET API so only admins can see auto-gen
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
@@ -64,26 +65,32 @@ func AlertRuleGen(mutators ...AlertRuleMutator) func() *AlertRule {
|
||||
panelID = &p
|
||||
}
|
||||
|
||||
var ns []NotificationSettings
|
||||
if rand.Int63()%2 == 0 {
|
||||
ns = append(ns, NotificationSettingsGen()())
|
||||
}
|
||||
|
||||
rule := &AlertRule{
|
||||
ID: rand.Int63n(1500),
|
||||
OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation.
|
||||
Title: "TEST-ALERT-" + util.GenerateShortUID(),
|
||||
Condition: "A",
|
||||
Data: []AlertQuery{GenerateAlertQuery()},
|
||||
Updated: time.Now().Add(-time.Duration(rand.Intn(100) + 1)),
|
||||
IntervalSeconds: rand.Int63n(60) + 1,
|
||||
Version: rand.Int63n(1500), // Don't generate a rule ID too big for postgres
|
||||
UID: util.GenerateShortUID(),
|
||||
NamespaceUID: util.GenerateShortUID(),
|
||||
DashboardUID: dashUID,
|
||||
PanelID: panelID,
|
||||
RuleGroup: "TEST-GROUP-" + util.GenerateShortUID(),
|
||||
RuleGroupIndex: rand.Intn(1500),
|
||||
NoDataState: randNoDataState(),
|
||||
ExecErrState: randErrState(),
|
||||
For: forInterval,
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
ID: rand.Int63n(1500),
|
||||
OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation.
|
||||
Title: "TEST-ALERT-" + util.GenerateShortUID(),
|
||||
Condition: "A",
|
||||
Data: []AlertQuery{GenerateAlertQuery()},
|
||||
Updated: time.Now().Add(-time.Duration(rand.Intn(100) + 1)),
|
||||
IntervalSeconds: rand.Int63n(60) + 1,
|
||||
Version: rand.Int63n(1500), // Don't generate a rule ID too big for postgres
|
||||
UID: util.GenerateShortUID(),
|
||||
NamespaceUID: util.GenerateShortUID(),
|
||||
DashboardUID: dashUID,
|
||||
PanelID: panelID,
|
||||
RuleGroup: "TEST-GROUP-" + util.GenerateShortUID(),
|
||||
RuleGroupIndex: rand.Intn(1500),
|
||||
NoDataState: randNoDataState(),
|
||||
ExecErrState: randErrState(),
|
||||
For: forInterval,
|
||||
Annotations: annotations,
|
||||
Labels: labels,
|
||||
NotificationSettings: ns,
|
||||
}
|
||||
|
||||
for _, mutator := range mutators {
|
||||
@@ -266,6 +273,20 @@ func WithUniqueUID(knownUids *sync.Map) AlertRuleMutator {
|
||||
}
|
||||
}
|
||||
|
||||
func WithUniqueTitle(knownTitles *sync.Map) AlertRuleMutator {
|
||||
return func(rule *AlertRule) {
|
||||
title := rule.Title
|
||||
for {
|
||||
_, ok := knownTitles.LoadOrStore(title, struct{}{})
|
||||
if !ok {
|
||||
rule.Title = title
|
||||
return
|
||||
}
|
||||
title = uuid.NewString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithQuery(query ...AlertQuery) AlertRuleMutator {
|
||||
return func(rule *AlertRule) {
|
||||
rule.Data = query
|
||||
@@ -283,6 +304,18 @@ func WithGroupKey(groupKey AlertRuleGroupKey) AlertRuleMutator {
|
||||
}
|
||||
}
|
||||
|
||||
func WithNotificationSettingsGen(ns func() NotificationSettings) AlertRuleMutator {
|
||||
return func(rule *AlertRule) {
|
||||
rule.NotificationSettings = []NotificationSettings{ns()}
|
||||
}
|
||||
}
|
||||
|
||||
func WithNoNotificationSettings() AlertRuleMutator {
|
||||
return func(rule *AlertRule) {
|
||||
rule.NotificationSettings = nil
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateAlertLabels(count int, prefix string) data.Labels {
|
||||
labels := make(data.Labels, count)
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -412,6 +445,10 @@ func CopyRule(r *AlertRule) *AlertRule {
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range r.NotificationSettings {
|
||||
result.NotificationSettings = append(result.NotificationSettings, CopyNotificationSettings(s))
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
@@ -601,3 +638,108 @@ func AlertInstanceGen(mutators ...AlertInstanceMutator) *AlertInstance {
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
type Mutator[T any] func(*T)
|
||||
|
||||
// CopyNotificationSettings creates a deep copy of NotificationSettings.
|
||||
func CopyNotificationSettings(ns NotificationSettings, mutators ...Mutator[NotificationSettings]) NotificationSettings {
|
||||
c := NotificationSettings{
|
||||
Receiver: ns.Receiver,
|
||||
}
|
||||
if ns.GroupWait != nil {
|
||||
c.GroupWait = util.Pointer(*ns.GroupWait)
|
||||
}
|
||||
if ns.GroupInterval != nil {
|
||||
c.GroupInterval = util.Pointer(*ns.GroupInterval)
|
||||
}
|
||||
if ns.RepeatInterval != nil {
|
||||
c.RepeatInterval = util.Pointer(*ns.RepeatInterval)
|
||||
}
|
||||
if ns.GroupBy != nil {
|
||||
c.GroupBy = make([]string, len(ns.GroupBy))
|
||||
copy(c.GroupBy, ns.GroupBy)
|
||||
}
|
||||
if ns.MuteTimeIntervals != nil {
|
||||
c.MuteTimeIntervals = make([]string, len(ns.MuteTimeIntervals))
|
||||
copy(c.MuteTimeIntervals, ns.MuteTimeIntervals)
|
||||
}
|
||||
for _, mutator := range mutators {
|
||||
mutator(&c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// NotificationSettingsGen generates NotificationSettings using a base and mutators.
|
||||
func NotificationSettingsGen(mutators ...Mutator[NotificationSettings]) func() NotificationSettings {
|
||||
return func() NotificationSettings {
|
||||
c := NotificationSettings{
|
||||
Receiver: util.GenerateShortUID(),
|
||||
GroupBy: []string{model.AlertNameLabel, FolderTitleLabel, util.GenerateShortUID()},
|
||||
GroupWait: util.Pointer(model.Duration(time.Duration(rand.Intn(100)+1) * time.Second)),
|
||||
GroupInterval: util.Pointer(model.Duration(time.Duration(rand.Intn(100)+1) * time.Second)),
|
||||
RepeatInterval: util.Pointer(model.Duration(time.Duration(rand.Intn(100)+1) * time.Second)),
|
||||
MuteTimeIntervals: []string{util.GenerateShortUID(), util.GenerateShortUID()},
|
||||
}
|
||||
for _, mutator := range mutators {
|
||||
mutator(&c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
NSMuts = NotificationSettingsMutators{}
|
||||
)
|
||||
|
||||
type NotificationSettingsMutators struct{}
|
||||
|
||||
func (n NotificationSettingsMutators) WithReceiver(receiver string) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
ns.Receiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
func (n NotificationSettingsMutators) WithGroupWait(groupWait *time.Duration) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
if groupWait == nil {
|
||||
ns.GroupWait = nil
|
||||
return
|
||||
}
|
||||
dur := model.Duration(*groupWait)
|
||||
ns.GroupWait = &dur
|
||||
}
|
||||
}
|
||||
|
||||
func (n NotificationSettingsMutators) WithGroupInterval(groupInterval *time.Duration) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
if groupInterval == nil {
|
||||
ns.GroupInterval = nil
|
||||
return
|
||||
}
|
||||
dur := model.Duration(*groupInterval)
|
||||
ns.GroupInterval = &dur
|
||||
}
|
||||
}
|
||||
|
||||
func (n NotificationSettingsMutators) WithRepeatInterval(repeatInterval *time.Duration) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
if repeatInterval == nil {
|
||||
ns.RepeatInterval = nil
|
||||
return
|
||||
}
|
||||
dur := model.Duration(*repeatInterval)
|
||||
ns.RepeatInterval = &dur
|
||||
}
|
||||
}
|
||||
|
||||
func (n NotificationSettingsMutators) WithGroupBy(groupBy ...string) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
ns.GroupBy = groupBy
|
||||
}
|
||||
}
|
||||
|
||||
func (n NotificationSettingsMutators) WithMuteTimeIntervals(muteTimeIntervals ...string) Mutator[NotificationSettings] {
|
||||
return func(ns *NotificationSettings) {
|
||||
ns.MuteTimeIntervals = muteTimeIntervals
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user