mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add integration test for AddDashAlertMigration (#47730)
* Alerting: Add integration test for AddDashAlertMigration * Add more targeted test cases * Apply suggestions from code review Co-authored-by: gotjosh <josue.abreu@gmail.com> * Reorganize file and improve comments * Replace custom sort+trim with go-cmp * Add test for AddDashAlertMigration * Rename test cases to standard format * Apply suggestions from code review Co-authored-by: gotjosh <josue.abreu@gmail.com> * Remove test-only snapshots of PostableUserConfig et al. * Organize imports * Fix linting Co-authored-by: gotjosh <josue.abreu@gmail.com>
This commit is contained in:
parent
b034bd18b5
commit
8972418113
599
pkg/services/sqlstore/migrations/ualert/migration_test.go
Normal file
599
pkg/services/sqlstore/migrations/ualert/migration_test.go
Normal file
@ -0,0 +1,599 @@
|
||||
package ualert_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations/ualert"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// TestAddDashAlertMigration tests the AddDashAlertMigration wrapper method that decides when to run the migration based on migration status and settings.
|
||||
func TestAddDashAlertMigration(t *testing.T) {
|
||||
x := setupTestDB(t)
|
||||
|
||||
tc := []struct {
|
||||
name string
|
||||
config *setting.Cfg
|
||||
isMigrationRun bool
|
||||
expected []string // set of migration titles
|
||||
}{
|
||||
{
|
||||
name: "when unified alerting enabled and migration not already run, then add main migration and clear rmMigration log entry",
|
||||
config: &setting.Cfg{
|
||||
UnifiedAlerting: setting.UnifiedAlertingSettings{
|
||||
Enabled: boolPointer(true),
|
||||
},
|
||||
},
|
||||
isMigrationRun: false,
|
||||
expected: []string{fmt.Sprintf(ualert.ClearMigrationEntryTitle, ualert.RmMigTitle), ualert.MigTitle},
|
||||
},
|
||||
{
|
||||
name: "when unified alerting disabled and migration is already run, then add rmMigration and clear main migration log entry",
|
||||
config: &setting.Cfg{
|
||||
UnifiedAlerting: setting.UnifiedAlertingSettings{
|
||||
Enabled: boolPointer(false),
|
||||
},
|
||||
},
|
||||
isMigrationRun: true,
|
||||
expected: []string{fmt.Sprintf(ualert.ClearMigrationEntryTitle, ualert.MigTitle), ualert.RmMigTitle},
|
||||
},
|
||||
{
|
||||
name: "when unified alerting enabled and migration is already run, then do nothing",
|
||||
config: &setting.Cfg{
|
||||
UnifiedAlerting: setting.UnifiedAlertingSettings{
|
||||
Enabled: boolPointer(true),
|
||||
},
|
||||
},
|
||||
isMigrationRun: true,
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "when unified alerting disabled and migration is not already run, then do nothing",
|
||||
config: &setting.Cfg{
|
||||
UnifiedAlerting: setting.UnifiedAlertingSettings{
|
||||
Enabled: boolPointer(false),
|
||||
},
|
||||
},
|
||||
isMigrationRun: false,
|
||||
expected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.isMigrationRun {
|
||||
log := migrator.MigrationLog{
|
||||
MigrationID: ualert.MigTitle,
|
||||
SQL: "",
|
||||
Timestamp: time.Now(),
|
||||
Success: true,
|
||||
}
|
||||
_, err := x.Insert(log)
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
_, err := x.Exec("DELETE FROM migration_log WHERE migration_id = ?", ualert.MigTitle)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mg := migrator.NewMigrator(x, tt.config)
|
||||
ualert.AddDashAlertMigration(mg)
|
||||
require.Equal(t, tt.expected, mg.GetMigrationIDs(false))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDashAlertMigration tests the execution of the main DashAlertMigration.
|
||||
func TestDashAlertMigration(t *testing.T) {
|
||||
// Run initial migration to have a working DB.
|
||||
x := setupTestDB(t)
|
||||
|
||||
emailSettings := `{"addresses": "test"}`
|
||||
slackSettings := `{"recipient": "test", "token": "test"}`
|
||||
opsgenieSettings := `{"apiKey": "test"}`
|
||||
|
||||
tc := []struct {
|
||||
name string
|
||||
legacyChannels []*models.AlertNotification
|
||||
alerts []*models.Alert
|
||||
|
||||
expected map[int64]*ualert.PostableUserConfig
|
||||
expErr error
|
||||
}{
|
||||
{
|
||||
name: "general multi-org, multi-alert, multi-channel migration",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, false),
|
||||
createAlertNotification(t, int64(1), "notifier2", "slack", slackSettings, false),
|
||||
createAlertNotification(t, int64(1), "notifier3", "opsgenie", opsgenieSettings, false),
|
||||
createAlertNotification(t, int64(2), "notifier4", "email", emailSettings, false),
|
||||
createAlertNotification(t, int64(2), "notifier5", "slack", slackSettings, false),
|
||||
createAlertNotification(t, int64(2), "notifier6", "opsgenie", opsgenieSettings, true), // default
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1"}),
|
||||
createAlert(t, int64(1), int64(1), int64(2), "alert2", []string{"notifier2", "notifier3"}),
|
||||
createAlert(t, int64(1), int64(2), int64(3), "alert3", []string{"notifier3"}),
|
||||
createAlert(t, int64(2), int64(3), int64(1), "alert4", []string{"notifier4"}),
|
||||
createAlert(t, int64(2), int64(3), int64(2), "alert5", []string{"notifier4", "notifier5", "notifier6"}),
|
||||
createAlert(t, int64(2), int64(4), int64(3), "alert6", []string{}),
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
Routes: []*ualert.Route{
|
||||
{Receiver: "autogen-contact-point-1", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert1")}, // These Matchers are temporary and will be replaced below with generated rule_uid.
|
||||
{Receiver: "autogen-contact-point-2", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert2")},
|
||||
{Receiver: "autogen-contact-point-3", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert3")},
|
||||
},
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}}, // email
|
||||
{Name: "autogen-contact-point-2", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier2", Type: "slack"}, {Name: "notifier3", Type: "opsgenie"}}}, // slack+opsgenie
|
||||
{Name: "autogen-contact-point-3", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier3", Type: "opsgenie"}}}, // opsgenie
|
||||
{Name: "autogen-contact-point-default"}, // empty default
|
||||
},
|
||||
},
|
||||
},
|
||||
int64(2): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
Routes: []*ualert.Route{
|
||||
{Receiver: "autogen-contact-point-4", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert4")},
|
||||
{Receiver: "autogen-contact-point-5", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert5")},
|
||||
},
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-4", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier4", Type: "email"}, {Name: "notifier6", Type: "opsgenie"}}}, // email
|
||||
{Name: "autogen-contact-point-5", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier4", Type: "email"}, {Name: "notifier5", Type: "slack"}, {Name: "notifier6", Type: "opsgenie"}}}, // email+slack+opsgenie
|
||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier6", Type: "opsgenie"}}}, // empty default
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when default channel, add to autogen-contact-point-default",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, true), // default
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1"}),
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when default channels exist alongside non-default, add only defaults to autogen-contact-point-default",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, true), // default
|
||||
createAlertNotification(t, int64(1), "notifier2", "slack", slackSettings, false),
|
||||
createAlertNotification(t, int64(1), "notifier3", "opsgenie", opsgenieSettings, true), // default
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier2"}), // + notifier1, notifier3
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
Routes: []*ualert.Route{
|
||||
{Receiver: "autogen-contact-point-1", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert1")},
|
||||
},
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}, {Name: "notifier3", Type: "opsgenie"}}},
|
||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier3", Type: "opsgenie"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when alerts share all channels, only create one receiver for all of them",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, false),
|
||||
createAlertNotification(t, int64(1), "notifier2", "slack", slackSettings, false),
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1", "notifier2"}),
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert2", []string{"notifier1", "notifier2"}),
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
Routes: []*ualert.Route{
|
||||
{Receiver: "autogen-contact-point-1", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert1")},
|
||||
{Receiver: "autogen-contact-point-1", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert2")},
|
||||
},
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}}},
|
||||
{Name: "autogen-contact-point-default"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when channel not linked to any alerts, migrate it to autogen-unlinked-channel-recv",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, true), // default
|
||||
createAlertNotification(t, int64(1), "notifier2", "slack", slackSettings, true), // default
|
||||
createAlertNotification(t, int64(1), "notifier3", "opsgenie", opsgenieSettings, false), // unlinked
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1"}),
|
||||
createAlert(t, int64(1), int64(2), int64(3), "alert3", []string{}),
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-default", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}, {Name: "notifier2", Type: "slack"}}},
|
||||
{Name: "autogen-unlinked-channel-recv", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier3", Type: "opsgenie"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when unsupported channels, do not migrate them",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "hipchat", "", false),
|
||||
createAlertNotification(t, int64(1), "notifier2", "sensu", "", false),
|
||||
},
|
||||
alerts: []*models.Alert{},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-default"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when unsupported channel linked to alert, do not migrate only that channel",
|
||||
legacyChannels: []*models.AlertNotification{
|
||||
createAlertNotification(t, int64(1), "notifier1", "email", emailSettings, false),
|
||||
createAlertNotification(t, int64(1), "notifier2", "sensu", "", false),
|
||||
},
|
||||
alerts: []*models.Alert{
|
||||
createAlert(t, int64(1), int64(1), int64(1), "alert1", []string{"notifier1", "notifier2"}),
|
||||
},
|
||||
expected: map[int64]*ualert.PostableUserConfig{
|
||||
int64(1): {
|
||||
AlertmanagerConfig: ualert.PostableApiAlertingConfig{
|
||||
Route: &ualert.Route{
|
||||
Receiver: "autogen-contact-point-default",
|
||||
Routes: []*ualert.Route{
|
||||
{Receiver: "autogen-contact-point-1", Matchers: newMatchers(labels.MatchEqual, "alert_name", "alert1")},
|
||||
},
|
||||
},
|
||||
Receivers: []*ualert.PostableApiReceiver{
|
||||
{Name: "autogen-contact-point-1", GrafanaManagedReceivers: []*ualert.PostableGrafanaReceiver{{Name: "notifier1", Type: "email"}}}, // no sensu
|
||||
{Name: "autogen-contact-point-default"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tc {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer teardown(t, x)
|
||||
setupLegacyAlertsTables(t, x, tt.legacyChannels, tt.alerts)
|
||||
|
||||
_, errDeleteMig := x.Exec("DELETE FROM migration_log WHERE migration_id = ?", ualert.MigTitle)
|
||||
require.NoError(t, errDeleteMig)
|
||||
|
||||
alertMigrator := migrator.NewMigrator(x, &setting.Cfg{})
|
||||
alertMigrator.AddMigration(ualert.RmMigTitle, &ualert.RmMigration{})
|
||||
ualert.AddDashAlertMigration(alertMigrator)
|
||||
|
||||
errRunningMig := alertMigrator.Start(false, 0)
|
||||
require.NoError(t, errRunningMig)
|
||||
|
||||
for orgId := range tt.expected {
|
||||
amConfig := getAlertmanagerConfig(t, x, orgId)
|
||||
|
||||
// Order of nested GrafanaManagedReceivers is not guaranteed.
|
||||
cOpt := []cmp.Option{
|
||||
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.PostableApiReceiver) bool { return a.Name < b.Name }),
|
||||
}
|
||||
if !cmp.Equal(tt.expected[orgId].AlertmanagerConfig.Receivers, amConfig.AlertmanagerConfig.Receivers, cOpt...) {
|
||||
t.Errorf("Unexpected Receivers: %v", cmp.Diff(tt.expected[orgId].AlertmanagerConfig.Receivers, amConfig.AlertmanagerConfig.Receivers, cOpt...))
|
||||
}
|
||||
|
||||
// Since routes and alerts are connecting solely by the Matchers on rule_uid, which is created at runtime we need to do some prep-work to populate the expected Matchers.
|
||||
alertUids := getAlertNameToUidMap(t, x, orgId)
|
||||
replaceAlertNameMatcherWithRuleUid(t, tt.expected[orgId].AlertmanagerConfig.Route.Routes, alertUids)
|
||||
|
||||
// Order of nested routes is not guaranteed.
|
||||
cOpt = []cmp.Option{
|
||||
cmpopts.SortSlices(func(a, b *ualert.Route) bool { return a.Receiver < b.Receiver }),
|
||||
cmpopts.IgnoreUnexported(ualert.Route{}, labels.Matcher{}),
|
||||
}
|
||||
if !cmp.Equal(tt.expected[orgId].AlertmanagerConfig.Route, amConfig.AlertmanagerConfig.Route, cOpt...) {
|
||||
t.Errorf("Unexpected Route: %v", cmp.Diff(tt.expected[orgId].AlertmanagerConfig.Route, amConfig.AlertmanagerConfig.Route, cOpt...))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// setupTestDB prepares the sqlite database and runs OSS migrations to initialize the schemas.
|
||||
func setupTestDB(t *testing.T) *xorm.Engine {
|
||||
t.Helper()
|
||||
testDB := sqlutil.SQLite3TestDB()
|
||||
|
||||
x, err := xorm.NewEngine(testDB.DriverName, testDB.ConnStr)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = migrator.NewDialect(x).CleanDB()
|
||||
require.NoError(t, err)
|
||||
|
||||
mg := migrator.NewMigrator(x, &setting.Cfg{})
|
||||
migrations := &migrations.OSSMigrations{}
|
||||
migrations.AddMigration(mg)
|
||||
|
||||
err = mg.Start(false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
var (
|
||||
now = time.Now()
|
||||
)
|
||||
|
||||
// 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 {
|
||||
t.Helper()
|
||||
settingsJson := simplejson.New()
|
||||
if settings != "" {
|
||||
s, err := simplejson.NewJson([]byte(settings))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal alert notification json: %v", err)
|
||||
}
|
||||
settingsJson = s
|
||||
}
|
||||
|
||||
return &models.AlertNotification{
|
||||
OrgId: orgId,
|
||||
Uid: uid,
|
||||
Name: uid, // Same as uid to make testing easier.
|
||||
Type: channelType,
|
||||
DisableResolveMessage: false,
|
||||
IsDefault: defaultChannel,
|
||||
Settings: settingsJson,
|
||||
SecureSettings: make(map[string][]byte),
|
||||
Created: now,
|
||||
Updated: now,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
t.Helper()
|
||||
|
||||
var settings = simplejson.New()
|
||||
if len(notifierUids) != 0 {
|
||||
notifiers := make([]interface{}, 0)
|
||||
for _, n := range notifierUids {
|
||||
notifiers = append(notifiers, struct {
|
||||
Uid string
|
||||
}{Uid: n})
|
||||
}
|
||||
|
||||
settings.Set("notifications", notifiers)
|
||||
}
|
||||
|
||||
return &models.Alert{
|
||||
OrgId: orgId,
|
||||
DashboardId: dashboardId,
|
||||
PanelId: panelsId,
|
||||
Name: name,
|
||||
Message: "message",
|
||||
Frequency: int64(60),
|
||||
For: time.Duration(time.Duration(60).Seconds()),
|
||||
State: models.AlertStateOK,
|
||||
Settings: settings,
|
||||
NewStateDate: now,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
}
|
||||
}
|
||||
|
||||
// createDashboard creates a dashboard for inserting into the test database.
|
||||
func createDashboard(t *testing.T, id int64, orgId int64, uid string) *models.Dashboard {
|
||||
t.Helper()
|
||||
return &models.Dashboard{
|
||||
Id: id,
|
||||
OrgId: orgId,
|
||||
Uid: uid,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Title: uid, // Not tested, needed to satisfy contraint.
|
||||
}
|
||||
}
|
||||
|
||||
// createDatasource creates a ddatasource for inserting into the test database.
|
||||
func createDatasource(t *testing.T, id int64, orgId int64, uid string) *models.DataSource {
|
||||
t.Helper()
|
||||
return &models.DataSource{
|
||||
Id: id,
|
||||
OrgId: orgId,
|
||||
Uid: uid,
|
||||
Created: now,
|
||||
Updated: now,
|
||||
Name: uid, // Not tested, needed to satisfy contraint.
|
||||
}
|
||||
}
|
||||
|
||||
// teardown cleans the input tables between test cases.
|
||||
func teardown(t *testing.T, x *xorm.Engine) {
|
||||
_, err := x.Exec("DELETE from alert")
|
||||
require.NoError(t, err)
|
||||
_, err = x.Exec("DELETE from alert_notification")
|
||||
require.NoError(t, err)
|
||||
_, err = x.Exec("DELETE from dashboard")
|
||||
require.NoError(t, err)
|
||||
_, err = x.Exec("DELETE from data_source")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// setupLegacyAlertsTables inserts data into the legacy alerting tables that is needed for testing the migration.
|
||||
func setupLegacyAlertsTables(t *testing.T, x *xorm.Engine, legacyChannels []*models.AlertNotification, alerts []*models.Alert) {
|
||||
t.Helper()
|
||||
|
||||
// Setup dashboards.
|
||||
dashboards := []models.Dashboard{
|
||||
*createDashboard(t, 1, 1, "dash1-1"),
|
||||
*createDashboard(t, 2, 1, "dash2-1"),
|
||||
*createDashboard(t, 3, 2, "dash3-2"),
|
||||
*createDashboard(t, 4, 2, "dash4-2"),
|
||||
}
|
||||
_, errDashboards := x.Insert(dashboards)
|
||||
require.NoError(t, errDashboards)
|
||||
|
||||
// Setup data_sources.
|
||||
dataSources := []models.DataSource{
|
||||
*createDatasource(t, 1, 1, "ds1-1"),
|
||||
*createDatasource(t, 2, 1, "ds2-1"),
|
||||
*createDatasource(t, 3, 2, "ds3-2"),
|
||||
*createDatasource(t, 4, 2, "ds4-2"),
|
||||
}
|
||||
_, errDataSourcess := x.Insert(dataSources)
|
||||
require.NoError(t, errDataSourcess)
|
||||
|
||||
if len(legacyChannels) > 0 {
|
||||
_, channelErr := x.Insert(legacyChannels)
|
||||
require.NoError(t, channelErr)
|
||||
}
|
||||
|
||||
if len(alerts) > 0 {
|
||||
_, alertErr := x.Insert(alerts)
|
||||
require.NoError(t, alertErr)
|
||||
}
|
||||
}
|
||||
|
||||
// getAlertmanagerConfig retreives the Alertmanager Config from the database for a given orgId.
|
||||
func getAlertmanagerConfig(t *testing.T, x *xorm.Engine, orgId int64) *ualert.PostableUserConfig {
|
||||
amConfig := ""
|
||||
_, err := x.Table("alert_configuration").Where("org_id = ?", orgId).Cols("alertmanager_configuration").Get(&amConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
config := ualert.PostableUserConfig{}
|
||||
err = json.Unmarshal([]byte(amConfig), &config)
|
||||
require.NoError(t, err)
|
||||
return &config
|
||||
}
|
||||
|
||||
// getAlertNameToUidMap fetches alert_rules from database to create map of alert.Name -> alert.Uid. This is needed as alert Uid is created during migration and is used to match routes to alerts.
|
||||
func getAlertNameToUidMap(t *testing.T, x *xorm.Engine, orgId int64) map[string]string {
|
||||
t.Helper()
|
||||
alerts := []struct {
|
||||
Title string
|
||||
Uid string
|
||||
}{}
|
||||
err := x.Table("alert_rule").Where("org_id = ?", orgId).Find(&alerts)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := make(map[string]string)
|
||||
for _, alert := range alerts {
|
||||
res[alert.Title] = alert.Uid
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// replaceAlertNameMatcherWithRuleUid replaces the stub matchers based on alert_name with the rule_uid's generated during migration.
|
||||
func replaceAlertNameMatcherWithRuleUid(t *testing.T, rts []*ualert.Route, alertUids map[string]string) {
|
||||
for _, rt := range rts {
|
||||
if len(rt.Matchers) > 0 {
|
||||
// Replace alert name matcher with generated rule_uid matcher
|
||||
for _, m := range rt.Matchers {
|
||||
if m.Name == "alert_name" {
|
||||
m.Name = "rule_uid"
|
||||
m.Value = alertUids[m.Value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse for nested routes.
|
||||
replaceAlertNameMatcherWithRuleUid(t, rt.Routes, alertUids)
|
||||
}
|
||||
}
|
||||
|
||||
func boolPointer(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// newMatchers creates a new ualert.Matchers given MatchType, name, and value.
|
||||
func newMatchers(t labels.MatchType, n, v string) ualert.Matchers {
|
||||
matcher, _ := labels.NewMatcher(t, n, v)
|
||||
return ualert.Matchers(labels.Matchers{matcher})
|
||||
}
|
@ -10,15 +10,14 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
pb "github.com/prometheus/alertmanager/silence/silencepb"
|
||||
"xorm.io/xorm"
|
||||
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
pb "github.com/prometheus/alertmanager/silence/silencepb"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
const GENERAL_FOLDER = "General Alerting"
|
||||
|
@ -1,7 +1,9 @@
|
||||
package ualert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"xorm.io/xorm"
|
||||
@ -11,10 +13,33 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var MigTitle = migTitle
|
||||
var RmMigTitle = rmMigTitle
|
||||
var ClearMigrationEntryTitle = clearMigrationEntryTitle
|
||||
|
||||
type RmMigration = rmMigration
|
||||
|
||||
func (m *Matchers) UnmarshalJSON(data []byte) error {
|
||||
var lines []string
|
||||
if err := json.Unmarshal(data, &lines); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, line := range lines {
|
||||
pm, err := labels.ParseMatchers(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*m = append(*m, pm...)
|
||||
}
|
||||
sort.Sort(labels.Matchers(*m))
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_validateAlertmanagerConfig(t *testing.T) {
|
||||
tc := []struct {
|
||||
name string
|
||||
|
Loading…
Reference in New Issue
Block a user