mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Improve legacy migration to include send reminder & frequency (#60275)
* Alerting: Improve legacy migration to include send reminder & frequency Legacy channel frequency is migrated to the channel's migrated route's repeat interval if send reminder is true. If send reminder is false, we pseudo-disable the repeat interval by setting it to a large value (1y). If there were no default channels, the root notification policy is still created with the default 4h repeat interval.
This commit is contained in:
parent
be1c5e13d5
commit
63ba3ccb58
@ -8,6 +8,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/alertmanager/pkg/labels"
|
"github.com/prometheus/alertmanager/pkg/labels"
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
@ -16,6 +17,11 @@ import (
|
|||||||
ngModels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngModels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DisabledRepeatInterval is a large duration that will be used as a pseudo-disable in case a legacy channel doesn't have SendReminders enabled.
|
||||||
|
DisabledRepeatInterval = model.Duration(time.Duration(8736) * time.Hour) // 1y
|
||||||
|
)
|
||||||
|
|
||||||
type notificationChannel struct {
|
type notificationChannel struct {
|
||||||
ID int64 `xorm:"id"`
|
ID int64 `xorm:"id"`
|
||||||
OrgID int64 `xorm:"org_id"`
|
OrgID int64 `xorm:"org_id"`
|
||||||
@ -26,6 +32,8 @@ type notificationChannel struct {
|
|||||||
IsDefault bool `xorm:"is_default"`
|
IsDefault bool `xorm:"is_default"`
|
||||||
Settings *simplejson.Json `xorm:"settings"`
|
Settings *simplejson.Json `xorm:"settings"`
|
||||||
SecureSettings SecureJsonData `xorm:"secure_settings"`
|
SecureSettings SecureJsonData `xorm:"secure_settings"`
|
||||||
|
SendReminder bool `xorm:"send_reminder"`
|
||||||
|
Frequency model.Duration `xorm:"frequency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// channelsPerOrg maps notification channels per organisation
|
// channelsPerOrg maps notification channels per organisation
|
||||||
@ -37,6 +45,12 @@ type defaultChannelsPerOrg map[int64][]*notificationChannel
|
|||||||
// uidOrID for both uid and ID, primarily used for mapping legacy channel to migrated receiver.
|
// uidOrID for both uid and ID, primarily used for mapping legacy channel to migrated receiver.
|
||||||
type uidOrID interface{}
|
type uidOrID interface{}
|
||||||
|
|
||||||
|
// channelReceiver is a convenience struct that contains a notificationChannel and its corresponding migrated PostableApiReceiver.
|
||||||
|
type channelReceiver struct {
|
||||||
|
channel *notificationChannel
|
||||||
|
receiver *PostableApiReceiver
|
||||||
|
}
|
||||||
|
|
||||||
// setupAlertmanagerConfigs creates Alertmanager configs with migrated receivers and routes.
|
// setupAlertmanagerConfigs creates Alertmanager configs with migrated receivers and routes.
|
||||||
func (m *migration) setupAlertmanagerConfigs(rulesPerOrg map[int64]map[*alertRule][]uidOrID) (amConfigsPerOrg, error) {
|
func (m *migration) setupAlertmanagerConfigs(rulesPerOrg map[int64]map[*alertRule][]uidOrID) (amConfigsPerOrg, error) {
|
||||||
// allChannels: channelUID -> channelConfig
|
// allChannels: channelUID -> channelConfig
|
||||||
@ -66,7 +80,9 @@ func (m *migration) setupAlertmanagerConfigs(rulesPerOrg map[int64]map[*alertRul
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
amConfig.AlertmanagerConfig.Receivers = receivers
|
for _, cr := range receivers {
|
||||||
|
amConfig.AlertmanagerConfig.Receivers = append(amConfig.AlertmanagerConfig.Receivers, cr.receiver)
|
||||||
|
}
|
||||||
|
|
||||||
defaultReceivers := make(map[string]struct{})
|
defaultReceivers := make(map[string]struct{})
|
||||||
defaultChannels, ok := defaultChannelsPerOrg[orgID]
|
defaultChannels, ok := defaultChannelsPerOrg[orgID]
|
||||||
@ -85,10 +101,10 @@ func (m *migration) setupAlertmanagerConfigs(rulesPerOrg map[int64]map[*alertRul
|
|||||||
amConfig.AlertmanagerConfig.Receivers = append(amConfig.AlertmanagerConfig.Receivers, defaultReceiver)
|
amConfig.AlertmanagerConfig.Receivers = append(amConfig.AlertmanagerConfig.Receivers, defaultReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, recv := range receivers {
|
for _, cr := range receivers {
|
||||||
route, err := createRoute(recv)
|
route, err := createRoute(cr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create route for receiver %s in orgId %d: %w", recv.Name, orgID, err)
|
return nil, fmt.Errorf("failed to create route for receiver %s in orgId %d: %w", cr.receiver.Name, orgID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
amConfigPerOrg[orgID].AlertmanagerConfig.Route.Routes = append(amConfigPerOrg[orgID].AlertmanagerConfig.Route.Routes, route)
|
amConfigPerOrg[orgID].AlertmanagerConfig.Route.Routes = append(amConfigPerOrg[orgID].AlertmanagerConfig.Route.Routes, route)
|
||||||
@ -141,7 +157,9 @@ func (m *migration) getNotificationChannelMap() (channelsPerOrg, defaultChannels
|
|||||||
disable_resolve_message,
|
disable_resolve_message,
|
||||||
is_default,
|
is_default,
|
||||||
settings,
|
settings,
|
||||||
secure_settings
|
secure_settings,
|
||||||
|
send_reminder,
|
||||||
|
frequency
|
||||||
FROM
|
FROM
|
||||||
alert_notification
|
alert_notification
|
||||||
`
|
`
|
||||||
@ -196,8 +214,8 @@ func (m *migration) createNotifier(c *notificationChannel) (*PostableGrafanaRece
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create one receiver for every unique notification channel.
|
// Create one receiver for every unique notification channel.
|
||||||
func (m *migration) createReceivers(allChannels []*notificationChannel) (map[uidOrID]*PostableApiReceiver, []*PostableApiReceiver, error) {
|
func (m *migration) createReceivers(allChannels []*notificationChannel) (map[uidOrID]*PostableApiReceiver, []channelReceiver, error) {
|
||||||
receivers := make([]*PostableApiReceiver, 0, len(allChannels))
|
receivers := make([]channelReceiver, 0, len(allChannels))
|
||||||
receiversMap := make(map[uidOrID]*PostableApiReceiver)
|
receiversMap := make(map[uidOrID]*PostableApiReceiver)
|
||||||
|
|
||||||
set := make(map[string]struct{}) // Used to deduplicate sanitized names.
|
set := make(map[string]struct{}) // Used to deduplicate sanitized names.
|
||||||
@ -218,20 +236,23 @@ func (m *migration) createReceivers(allChannels []*notificationChannel) (map[uid
|
|||||||
|
|
||||||
set[sanitizedName] = struct{}{}
|
set[sanitizedName] = struct{}{}
|
||||||
|
|
||||||
recv := &PostableApiReceiver{
|
cr := channelReceiver{
|
||||||
Name: sanitizedName, // Channel name is unique within an Org.
|
channel: c,
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{notifier},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: sanitizedName, // Channel name is unique within an Org.
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{notifier},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
receivers = append(receivers, recv)
|
receivers = append(receivers, cr)
|
||||||
|
|
||||||
// Store receivers for creating routes from alert rules later.
|
// Store receivers for creating routes from alert rules later.
|
||||||
if c.Uid != "" {
|
if c.Uid != "" {
|
||||||
receiversMap[c.Uid] = recv
|
receiversMap[c.Uid] = cr.receiver
|
||||||
}
|
}
|
||||||
if c.ID != 0 {
|
if c.ID != 0 {
|
||||||
// In certain circumstances, the alert rule uses ID instead of uid. So, we add this to be able to lookup by ID in case.
|
// In certain circumstances, the alert rule uses ID instead of uid. So, we add this to be able to lookup by ID in case.
|
||||||
receiversMap[c.ID] = recv
|
receiversMap[c.ID] = cr.receiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,17 +261,27 @@ func (m *migration) createReceivers(allChannels []*notificationChannel) (map[uid
|
|||||||
|
|
||||||
// Create the root-level route with the default receiver. If no new receiver is created specifically for the root-level route, the returned receiver will be nil.
|
// Create the root-level route with the default receiver. If no new receiver is created specifically for the root-level route, the returned receiver will be nil.
|
||||||
func (m *migration) createDefaultRouteAndReceiver(defaultChannels []*notificationChannel) (*PostableApiReceiver, *Route, error) {
|
func (m *migration) createDefaultRouteAndReceiver(defaultChannels []*notificationChannel) (*PostableApiReceiver, *Route, error) {
|
||||||
var defaultReceiver *PostableApiReceiver
|
|
||||||
|
|
||||||
defaultReceiverName := "autogen-contact-point-default"
|
defaultReceiverName := "autogen-contact-point-default"
|
||||||
if len(defaultChannels) != 1 {
|
defaultRoute := &Route{
|
||||||
// If there are zero or more than one default channels we create a separate contact group that is used only in the root policy. This is to simplify the migrated notification policy structure.
|
Receiver: defaultReceiverName,
|
||||||
// If we ever allow more than one receiver per route this won't be necessary.
|
Routes: make([]*Route, 0),
|
||||||
defaultReceiver = &PostableApiReceiver{
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel}, // To keep parity with pre-migration notifications.
|
||||||
Name: defaultReceiverName,
|
RepeatInterval: nil,
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{},
|
}
|
||||||
}
|
newDefaultReceiver := &PostableApiReceiver{
|
||||||
|
Name: defaultReceiverName,
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return early if there are no default channels
|
||||||
|
if len(defaultChannels) == 0 {
|
||||||
|
return newDefaultReceiver, defaultRoute, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repeatInterval := DisabledRepeatInterval // If no channels have SendReminders enabled, we will use this large value as a pseudo-disable.
|
||||||
|
if len(defaultChannels) > 1 {
|
||||||
|
// If there are more than one default channels we create a separate contact group that is used only in the root policy. This is to simplify the migrated notification policy structure.
|
||||||
|
// If we ever allow more than one receiver per route this won't be necessary.
|
||||||
for _, c := range defaultChannels {
|
for _, c := range defaultChannels {
|
||||||
// Need to create a new notifier to prevent uid conflict.
|
// Need to create a new notifier to prevent uid conflict.
|
||||||
defaultNotifier, err := m.createNotifier(c)
|
defaultNotifier, err := m.createNotifier(c)
|
||||||
@ -258,39 +289,51 @@ func (m *migration) createDefaultRouteAndReceiver(defaultChannels []*notificatio
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultReceiver.GrafanaManagedReceivers = append(defaultReceiver.GrafanaManagedReceivers, defaultNotifier)
|
newDefaultReceiver.GrafanaManagedReceivers = append(newDefaultReceiver.GrafanaManagedReceivers, defaultNotifier)
|
||||||
|
|
||||||
|
// Choose the lowest send reminder duration from all the notifiers to use for default route.
|
||||||
|
if c.SendReminder && c.Frequency < repeatInterval {
|
||||||
|
repeatInterval = c.Frequency
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If there is only a single default channel, we don't need a separate receiver to hold it. We can reuse the existing receiver for that single notifier.
|
// If there is only a single default channel, we don't need a separate receiver to hold it. We can reuse the existing receiver for that single notifier.
|
||||||
defaultReceiverName = defaultChannels[0].Name
|
defaultRoute.Receiver = defaultChannels[0].Name
|
||||||
}
|
if defaultChannels[0].SendReminder {
|
||||||
|
repeatInterval = defaultChannels[0].Frequency
|
||||||
|
}
|
||||||
|
|
||||||
defaultRoute := &Route{
|
// No need to create a new receiver.
|
||||||
Receiver: defaultReceiverName,
|
newDefaultReceiver = nil
|
||||||
Routes: make([]*Route, 0),
|
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel}, // To keep parity with pre-migration notifications.
|
|
||||||
}
|
}
|
||||||
|
defaultRoute.RepeatInterval = &repeatInterval
|
||||||
|
|
||||||
return defaultReceiver, defaultRoute, nil
|
return newDefaultReceiver, defaultRoute, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create one route per contact point, matching based on ContactLabel.
|
// Create one route per contact point, matching based on ContactLabel.
|
||||||
func createRoute(recv *PostableApiReceiver) (*Route, error) {
|
func createRoute(cr channelReceiver) (*Route, error) {
|
||||||
// We create a regex matcher so that each alert rule need only have a single ContactLabel entry for all contact points it sends to.
|
// We create a regex matcher so that each alert rule need only have a single ContactLabel entry for all contact points it sends to.
|
||||||
// For example, if an alert needs to send to contact1 and contact2 it will have ContactLabel=`"contact1","contact2"` and will match both routes looking
|
// For example, if an alert needs to send to contact1 and contact2 it will have ContactLabel=`"contact1","contact2"` and will match both routes looking
|
||||||
// for `.*"contact1".*` and `.*"contact2".*`.
|
// for `.*"contact1".*` and `.*"contact2".*`.
|
||||||
|
|
||||||
// We quote and escape here to ensure the regex will correctly match the ContactLabel on the alerts.
|
// We quote and escape here to ensure the regex will correctly match the ContactLabel on the alerts.
|
||||||
name := fmt.Sprintf(`.*%s.*`, regexp.QuoteMeta(quote(recv.Name)))
|
name := fmt.Sprintf(`.*%s.*`, regexp.QuoteMeta(quote(cr.receiver.Name)))
|
||||||
mat, err := labels.NewMatcher(labels.MatchRegexp, ContactLabel, name)
|
mat, err := labels.NewMatcher(labels.MatchRegexp, ContactLabel, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repeatInterval := DisabledRepeatInterval
|
||||||
|
if cr.channel.SendReminder {
|
||||||
|
repeatInterval = cr.channel.Frequency
|
||||||
|
}
|
||||||
|
|
||||||
return &Route{
|
return &Route{
|
||||||
Receiver: recv.Name,
|
Receiver: cr.receiver.Name,
|
||||||
ObjectMatchers: ObjectMatchers{mat},
|
ObjectMatchers: ObjectMatchers{mat},
|
||||||
Continue: true, // We continue so that each sibling contact point route can separately match.
|
Continue: true, // We continue so that each sibling contact point route can separately match.
|
||||||
|
RepeatInterval: &repeatInterval,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,11 +473,12 @@ type PostableApiAlertingConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Receiver string `yaml:"receiver,omitempty" json:"receiver,omitempty"`
|
Receiver string `yaml:"receiver,omitempty" json:"receiver,omitempty"`
|
||||||
ObjectMatchers ObjectMatchers `yaml:"object_matchers,omitempty" json:"object_matchers,omitempty"`
|
ObjectMatchers ObjectMatchers `yaml:"object_matchers,omitempty" json:"object_matchers,omitempty"`
|
||||||
Routes []*Route `yaml:"routes,omitempty" json:"routes,omitempty"`
|
Routes []*Route `yaml:"routes,omitempty" json:"routes,omitempty"`
|
||||||
Continue bool `yaml:"continue,omitempty" json:"continue,omitempty"`
|
Continue bool `yaml:"continue,omitempty" json:"continue,omitempty"`
|
||||||
GroupByStr []string `yaml:"group_by,omitempty" json:"group_by,omitempty"`
|
GroupByStr []string `yaml:"group_by,omitempty" json:"group_by,omitempty"`
|
||||||
|
RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectMatchers labels.Matchers
|
type ObjectMatchers labels.Matchers
|
||||||
|
@ -2,6 +2,7 @@ package ualert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
@ -121,11 +122,13 @@ func TestFilterReceiversForAlert(t *testing.T) {
|
|||||||
func TestCreateRoute(t *testing.T) {
|
func TestCreateRoute(t *testing.T) {
|
||||||
tc := []struct {
|
tc := []struct {
|
||||||
name string
|
name string
|
||||||
|
channel *notificationChannel
|
||||||
recv *PostableApiReceiver
|
recv *PostableApiReceiver
|
||||||
expected *Route
|
expected *Route
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "when a receiver is passed in, the route should regex match based on quoted name with continue=true",
|
name: "when a receiver is passed in, the route should regex match based on quoted name with continue=true",
|
||||||
|
channel: ¬ificationChannel{},
|
||||||
recv: &PostableApiReceiver{
|
recv: &PostableApiReceiver{
|
||||||
Name: "recv1",
|
Name: "recv1",
|
||||||
},
|
},
|
||||||
@ -135,10 +138,12 @@ func TestCreateRoute(t *testing.T) {
|
|||||||
Routes: nil,
|
Routes: nil,
|
||||||
Continue: true,
|
Continue: true,
|
||||||
GroupByStr: nil,
|
GroupByStr: nil,
|
||||||
|
RepeatInterval: durationPointer(DisabledRepeatInterval),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "notification channel should be escaped for regex in the matcher",
|
name: "notification channel should be escaped for regex in the matcher",
|
||||||
|
channel: ¬ificationChannel{},
|
||||||
recv: &PostableApiReceiver{
|
recv: &PostableApiReceiver{
|
||||||
Name: `. ^ $ * + - ? ( ) [ ] { } \ |`,
|
Name: `. ^ $ * + - ? ( ) [ ] { } \ |`,
|
||||||
},
|
},
|
||||||
@ -148,13 +153,47 @@ func TestCreateRoute(t *testing.T) {
|
|||||||
Routes: nil,
|
Routes: nil,
|
||||||
Continue: true,
|
Continue: true,
|
||||||
GroupByStr: nil,
|
GroupByStr: nil,
|
||||||
|
RepeatInterval: durationPointer(DisabledRepeatInterval),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when a channel has sendReminder=true, the route should use the frequency in repeat interval",
|
||||||
|
channel: ¬ificationChannel{SendReminder: true, Frequency: model.Duration(time.Duration(42) * time.Hour)},
|
||||||
|
recv: &PostableApiReceiver{
|
||||||
|
Name: "recv1",
|
||||||
|
},
|
||||||
|
expected: &Route{
|
||||||
|
Receiver: "recv1",
|
||||||
|
ObjectMatchers: ObjectMatchers{{Type: 2, Name: ContactLabel, Value: `.*"recv1".*`}},
|
||||||
|
Routes: nil,
|
||||||
|
Continue: true,
|
||||||
|
GroupByStr: nil,
|
||||||
|
RepeatInterval: durationPointer(model.Duration(time.Duration(42) * time.Hour)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when a channel has sendReminder=false, the route should ignore the frequency in repeat interval and use DisabledRepeatInterval",
|
||||||
|
channel: ¬ificationChannel{SendReminder: false, Frequency: model.Duration(time.Duration(42) * time.Hour)},
|
||||||
|
recv: &PostableApiReceiver{
|
||||||
|
Name: "recv1",
|
||||||
|
},
|
||||||
|
expected: &Route{
|
||||||
|
Receiver: "recv1",
|
||||||
|
ObjectMatchers: ObjectMatchers{{Type: 2, Name: ContactLabel, Value: `.*"recv1".*`}},
|
||||||
|
Routes: nil,
|
||||||
|
Continue: true,
|
||||||
|
GroupByStr: nil,
|
||||||
|
RepeatInterval: durationPointer(DisabledRepeatInterval),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tc {
|
for _, tt := range tc {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
res, err := createRoute(tt.recv)
|
res, err := createRoute(channelReceiver{
|
||||||
|
channel: tt.channel,
|
||||||
|
receiver: tt.recv,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Order of nested routes is not guaranteed.
|
// Order of nested routes is not guaranteed.
|
||||||
@ -180,13 +219,18 @@ func createNotChannel(t *testing.T, uid string, id int64, name string) *notifica
|
|||||||
return ¬ificationChannel{Uid: uid, ID: id, Name: name, Settings: simplejson.New()}
|
return ¬ificationChannel{Uid: uid, ID: id, Name: name, Settings: simplejson.New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createNotChannelWithReminder(t *testing.T, uid string, id int64, name string, frequency model.Duration) *notificationChannel {
|
||||||
|
t.Helper()
|
||||||
|
return ¬ificationChannel{Uid: uid, ID: id, Name: name, SendReminder: true, Frequency: frequency, Settings: simplejson.New()}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateReceivers(t *testing.T) {
|
func TestCreateReceivers(t *testing.T) {
|
||||||
tc := []struct {
|
tc := []struct {
|
||||||
name string
|
name string
|
||||||
allChannels []*notificationChannel
|
allChannels []*notificationChannel
|
||||||
defaultChannels []*notificationChannel
|
defaultChannels []*notificationChannel
|
||||||
expRecvMap map[uidOrID]*PostableApiReceiver
|
expRecvMap map[uidOrID]*PostableApiReceiver
|
||||||
expRecv []*PostableApiReceiver
|
expRecv []channelReceiver
|
||||||
expErr error
|
expErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -210,14 +254,20 @@ func TestCreateReceivers(t *testing.T) {
|
|||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name2"}},
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name2"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expRecv: []*PostableApiReceiver{
|
expRecv: []channelReceiver{
|
||||||
{
|
{
|
||||||
Name: "name1",
|
channel: createNotChannel(t, "uid1", int64(1), "name1"),
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name1"}},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: "name1",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name1"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "name2",
|
channel: createNotChannel(t, "uid2", int64(2), "name2"),
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name2"}},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: "name2",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name2"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -234,10 +284,13 @@ func TestCreateReceivers(t *testing.T) {
|
|||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expRecv: []*PostableApiReceiver{
|
expRecv: []channelReceiver{
|
||||||
{
|
{
|
||||||
Name: "name_1",
|
channel: createNotChannel(t, "uid1", int64(1), "name\"1"),
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: "name_1",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -262,14 +315,20 @@ func TestCreateReceivers(t *testing.T) {
|
|||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1_dba13d"}},
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1_dba13d"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expRecv: []*PostableApiReceiver{
|
expRecv: []channelReceiver{
|
||||||
{
|
{
|
||||||
Name: "name_1",
|
channel: createNotChannel(t, "uid1", int64(1), "name\"1"),
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: "name_1",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "name_1_dba13d",
|
channel: createNotChannel(t, "uid2", int64(2), "name_1"),
|
||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1_dba13d"}},
|
receiver: &PostableApiReceiver{
|
||||||
|
Name: "name_1_dba13d",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name_1_dba13d"}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -289,7 +348,7 @@ func TestCreateReceivers(t *testing.T) {
|
|||||||
|
|
||||||
// We ignore certain fields for the purposes of this test
|
// We ignore certain fields for the purposes of this test
|
||||||
for _, recv := range recvs {
|
for _, recv := range recvs {
|
||||||
for _, not := range recv.GrafanaManagedReceivers {
|
for _, not := range recv.receiver.GrafanaManagedReceivers {
|
||||||
not.UID = ""
|
not.UID = ""
|
||||||
not.Settings = nil
|
not.Settings = nil
|
||||||
not.SecureSettings = nil
|
not.SecureSettings = nil
|
||||||
@ -319,9 +378,27 @@ func TestCreateDefaultRouteAndReceiver(t *testing.T) {
|
|||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name1"}, {Name: "name2"}},
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name1"}, {Name: "name2"}},
|
||||||
},
|
},
|
||||||
expRoute: &Route{
|
expRoute: &Route{
|
||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
Routes: make([]*Route, 0),
|
Routes: make([]*Route, 0),
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
RepeatInterval: durationPointer(DisabledRepeatInterval),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when given multiple default notification channels migrate them to a single receiver with RepeatInterval set to be the minimum of all channel frequencies",
|
||||||
|
defaultChannels: []*notificationChannel{
|
||||||
|
createNotChannelWithReminder(t, "uid1", int64(1), "name1", model.Duration(42)),
|
||||||
|
createNotChannelWithReminder(t, "uid2", int64(2), "name2", model.Duration(100000)),
|
||||||
|
},
|
||||||
|
expRecv: &PostableApiReceiver{
|
||||||
|
Name: "autogen-contact-point-default",
|
||||||
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{{Name: "name1"}, {Name: "name2"}},
|
||||||
|
},
|
||||||
|
expRoute: &Route{
|
||||||
|
Receiver: "autogen-contact-point-default",
|
||||||
|
Routes: make([]*Route, 0),
|
||||||
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
RepeatInterval: durationPointer(model.Duration(42)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -332,9 +409,10 @@ func TestCreateDefaultRouteAndReceiver(t *testing.T) {
|
|||||||
GrafanaManagedReceivers: []*PostableGrafanaReceiver{},
|
GrafanaManagedReceivers: []*PostableGrafanaReceiver{},
|
||||||
},
|
},
|
||||||
expRoute: &Route{
|
expRoute: &Route{
|
||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
Routes: make([]*Route, 0),
|
Routes: make([]*Route, 0),
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
RepeatInterval: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -342,9 +420,21 @@ func TestCreateDefaultRouteAndReceiver(t *testing.T) {
|
|||||||
defaultChannels: []*notificationChannel{createNotChannel(t, "uid1", int64(1), "name1")},
|
defaultChannels: []*notificationChannel{createNotChannel(t, "uid1", int64(1), "name1")},
|
||||||
expRecv: nil,
|
expRecv: nil,
|
||||||
expRoute: &Route{
|
expRoute: &Route{
|
||||||
Receiver: "name1",
|
Receiver: "name1",
|
||||||
Routes: make([]*Route, 0),
|
Routes: make([]*Route, 0),
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
RepeatInterval: durationPointer(DisabledRepeatInterval),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when given a single default notification channel with SendReminder=true, use the channels Frequency as the RepeatInterval",
|
||||||
|
defaultChannels: []*notificationChannel{createNotChannelWithReminder(t, "uid1", int64(1), "name1", model.Duration(42))},
|
||||||
|
expRecv: nil,
|
||||||
|
expRoute: &Route{
|
||||||
|
Receiver: "name1",
|
||||||
|
Routes: make([]*Route, 0),
|
||||||
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
RepeatInterval: durationPointer(model.Duration(42)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -375,3 +465,7 @@ func TestCreateDefaultRouteAndReceiver(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func durationPointer(d model.Duration) *model.Duration {
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
@ -158,10 +158,11 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier3", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier3".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier3", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier3".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: nil,
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
@ -177,15 +178,16 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "notifier6",
|
Receiver: "notifier6",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier4", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier4".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier4", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier4".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier5", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier5".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier5", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier5".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier6", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier6".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier6", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier6".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: durationPointer(ualert.DisabledRepeatInterval),
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier4", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier4", Type: "email"}}},
|
{Name: "notifier4", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier4", Type: "email"}}},
|
||||||
{Name: "notifier5", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier5", Type: "slack"}}},
|
{Name: "notifier5", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier5", Type: "slack"}}},
|
||||||
{Name: "notifier6", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier6", Type: "opsgenie"}}}, // empty default
|
{Name: "notifier6", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier6", Type: "opsgenie"}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -204,8 +206,9 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: nil,
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
@ -228,8 +231,33 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "notifier1",
|
Receiver: "notifier1",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: durationPointer(ualert.DisabledRepeatInterval),
|
||||||
|
},
|
||||||
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when single default channel with SendReminder, use channel Frequency as RepeatInterval",
|
||||||
|
legacyChannels: []*models.AlertNotification{
|
||||||
|
createAlertNotificationWithReminder(t, int64(1), "notifier1", "email", emailSettings, true, true, time.Duration(1)*time.Hour),
|
||||||
|
},
|
||||||
|
alerts: []*models.Alert{},
|
||||||
|
expected: map[int64]*ualert.PostableUserConfig{
|
||||||
|
int64(1): {
|
||||||
|
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||||
|
Route: &ualert.Route{
|
||||||
|
Receiver: "notifier1",
|
||||||
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
Routes: []*ualert.Route{
|
||||||
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(model.Duration(time.Duration(1) * time.Hour))},
|
||||||
|
},
|
||||||
|
RepeatInterval: durationPointer(model.Duration(time.Duration(1) * time.Hour)),
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
@ -252,9 +280,38 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: durationPointer(ualert.DisabledRepeatInterval),
|
||||||
|
},
|
||||||
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
|
{Name: "notifier2", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier2", Type: "slack"}}},
|
||||||
|
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when multiple default channels with SendReminder, use minimum channel frequency as RepeatInterval",
|
||||||
|
legacyChannels: []*models.AlertNotification{
|
||||||
|
createAlertNotificationWithReminder(t, int64(1), "notifier1", "email", emailSettings, true, true, time.Duration(1)*time.Hour),
|
||||||
|
createAlertNotificationWithReminder(t, int64(1), "notifier2", "slack", slackSettings, true, true, time.Duration(30)*time.Minute),
|
||||||
|
},
|
||||||
|
alerts: []*models.Alert{},
|
||||||
|
expected: map[int64]*ualert.PostableUserConfig{
|
||||||
|
int64(1): {
|
||||||
|
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||||
|
Route: &ualert.Route{
|
||||||
|
Receiver: "autogen-contact-point-default",
|
||||||
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
|
Routes: []*ualert.Route{
|
||||||
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(model.Duration(time.Duration(1) * time.Hour))},
|
||||||
|
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(model.Duration(time.Duration(30) * time.Minute))},
|
||||||
|
},
|
||||||
|
RepeatInterval: durationPointer(model.Duration(time.Duration(30) * time.Minute)),
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
@ -280,10 +337,11 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier3", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier3".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier3", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier3".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
|
RepeatInterval: durationPointer(ualert.DisabledRepeatInterval),
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||||
@ -294,36 +352,6 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "when alert has only defaults, don't create route for it",
|
|
||||||
legacyChannels: []*models.AlertNotification{
|
|
||||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, true), // default
|
|
||||||
createAlertNotification(t, int64(1), "notifier2", "slack", slackSettings, true), // default
|
|
||||||
},
|
|
||||||
alerts: []*models.Alert{
|
|
||||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1"}),
|
|
||||||
createAlert(t, int64(1), int64(2), int64(3), "alert2", []string{}),
|
|
||||||
},
|
|
||||||
expected: map[int64]*ualert.PostableUserConfig{
|
|
||||||
int64(1): {
|
|
||||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
|
||||||
Route: &ualert.Route{
|
|
||||||
Receiver: "autogen-contact-point-default",
|
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
|
||||||
Routes: []*ualert.Route{
|
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
|
||||||
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
|
||||||
{Name: "notifier1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
|
||||||
{Name: "notifier2", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier2", Type: "slack"}}},
|
|
||||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "when alerts share channels, only create one receiver per legacy channel",
|
name: "when alerts share channels, only create one receiver per legacy channel",
|
||||||
legacyChannels: []*models.AlertNotification{
|
legacyChannels: []*models.AlertNotification{
|
||||||
@ -341,8 +369,8 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier2", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier2".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
@ -367,7 +395,7 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
@ -393,7 +421,7 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
@ -420,7 +448,7 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
Receiver: "autogen-contact-point-default",
|
Receiver: "autogen-contact-point-default",
|
||||||
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
GroupByStr: []string{ngModels.FolderTitleLabel, model.AlertNameLabel},
|
||||||
Routes: []*ualert.Route{
|
Routes: []*ualert.Route{
|
||||||
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true},
|
{Receiver: "notifier1", ObjectMatchers: ualert.ObjectMatchers{{Type: 2, Name: ualert.ContactLabel, Value: `.*"notifier1".*`}}, Routes: nil, Continue: true, RepeatInterval: durationPointer(ualert.DisabledRepeatInterval)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Receivers: []*ualert.PostableApiReceiver{
|
Receivers: []*ualert.PostableApiReceiver{
|
||||||
@ -444,6 +472,7 @@ func TestAMConfigMigration(t *testing.T) {
|
|||||||
|
|
||||||
// Order of nested GrafanaManagedReceivers is not guaranteed.
|
// Order of nested GrafanaManagedReceivers is not guaranteed.
|
||||||
cOpt := []cmp.Option{
|
cOpt := []cmp.Option{
|
||||||
|
cmpopts.IgnoreUnexported(ualert.PostableApiReceiver{}),
|
||||||
cmpopts.IgnoreFields(ualert.PostableGrafanaReceiver{}, "UID", "Settings", "SecureSettings"),
|
cmpopts.IgnoreFields(ualert.PostableGrafanaReceiver{}, "UID", "Settings", "SecureSettings"),
|
||||||
cmpopts.SortSlices(func(a, b *ualert.PostableGrafanaReceiver) bool { return a.Name < b.Name }),
|
cmpopts.SortSlices(func(a, b *ualert.PostableGrafanaReceiver) bool { return a.Name < b.Name }),
|
||||||
cmpopts.SortSlices(func(a, b *ualert.PostableApiReceiver) bool { return a.Name < b.Name }),
|
cmpopts.SortSlices(func(a, b *ualert.PostableApiReceiver) bool { return a.Name < b.Name }),
|
||||||
@ -576,8 +605,8 @@ var (
|
|||||||
now = time.Now()
|
now = time.Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
// createAlertNotification creates a legacy alert notification channel for inserting into the test database.
|
// createAlertNotificationWithReminder creates a legacy alert notification channel for inserting into the test database.
|
||||||
func createAlertNotification(t *testing.T, orgId int64, uid string, channelType string, settings string, defaultChannel bool) *models.AlertNotification {
|
func createAlertNotificationWithReminder(t *testing.T, orgId int64, uid string, channelType string, settings string, defaultChannel bool, sendReminder bool, frequency time.Duration) *models.AlertNotification {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
settingsJson := simplejson.New()
|
settingsJson := simplejson.New()
|
||||||
if settings != "" {
|
if settings != "" {
|
||||||
@ -599,9 +628,16 @@ func createAlertNotification(t *testing.T, orgId int64, uid string, channelType
|
|||||||
SecureSettings: make(map[string][]byte),
|
SecureSettings: make(map[string][]byte),
|
||||||
Created: now,
|
Created: now,
|
||||||
Updated: now,
|
Updated: now,
|
||||||
|
SendReminder: sendReminder,
|
||||||
|
Frequency: frequency,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createAlertNotification creates a legacy alert notification channel for inserting into the test database.
|
||||||
|
func createAlertNotification(t *testing.T, orgId int64, uid string, channelType string, settings string, defaultChannel bool) *models.AlertNotification {
|
||||||
|
return createAlertNotificationWithReminder(t, orgId, uid, channelType, settings, defaultChannel, false, time.Duration(0))
|
||||||
|
}
|
||||||
|
|
||||||
// createAlert creates a legacy alert rule for inserting into the test database.
|
// createAlert creates a legacy alert rule for inserting into the test database.
|
||||||
func createAlert(t *testing.T, orgId int64, dashboardId int64, panelsId int64, name string, notifierUids []string) *models.Alert {
|
func createAlert(t *testing.T, orgId int64, dashboardId int64, panelsId int64, name string, notifierUids []string) *models.Alert {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@ -766,3 +802,7 @@ func getAlertRules(t *testing.T, x *xorm.Engine, orgId int64) []*ngModels.AlertR
|
|||||||
func boolPointer(b bool) *bool {
|
func boolPointer(b bool) *bool {
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func durationPointer(d model.Duration) *model.Duration {
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user