[v8.5.x] Alerting: Update migration to put alerts to the default folder if dashboard folder is missing (#66616)
Alerting: Update migration to put alerts to the default folder if dashboard folder is missing (#65577)
* extract function
* use context logger
* put alert to general folder if folder is missing
* move folderHelper init
* add test
* Update pkg/services/sqlstore/migrations/ualert/ualert.go
Co-authored-by: Matthew Jacobson <matthew.jacobson@grafana.com>
---------
Co-authored-by: Matthew Jacobson <matthew.jacobson@grafana.com>
(cherry picked from commit 7b2f44762e)
# Conflicts:
# pkg/services/sqlstore/migrations/ualert/migration_test.go
# pkg/services/sqlstore/migrations/ualert/ualert.go
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ngModels "github.com/grafana/grafana/pkg/services/ngalert/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"
|
||||
@@ -459,6 +460,49 @@ func TestDashAlertMigration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestDashAlertMigration tests the execution of the main DashAlertMigration specifically for migrations of models.
|
||||
func TestDashAlertMigrationFolders(t *testing.T) {
|
||||
// Run initial migration to have a working DB.
|
||||
x := setupTestDB(t)
|
||||
|
||||
t.Run("when folder is missing put alert in General folder", func(t *testing.T) {
|
||||
o := createOrg(t, 1)
|
||||
folder1 := createDashboard(t, 1, o.Id, "folder-1")
|
||||
folder1.IsFolder = true
|
||||
dash1 := createDashboard(t, 3, o.Id, "dash1")
|
||||
dash1.FolderId = folder1.Id
|
||||
dash2 := createDashboard(t, 4, o.Id, "dash2")
|
||||
dash2.FolderId = 22 // missing folder
|
||||
|
||||
a1 := createAlert(t, o.Id, dash1.Id, int64(1), "alert-1", []string{})
|
||||
a2 := createAlert(t, o.Id, dash2.Id, int64(1), "alert-2", []string{})
|
||||
|
||||
_, err := x.Insert(o, folder1, dash1, dash2, a1, a2)
|
||||
require.NoError(t, err)
|
||||
|
||||
runDashAlertMigrationTestRun(t, x)
|
||||
|
||||
rules := getAlertRules(t, x, o.Id)
|
||||
require.Len(t, rules, 2)
|
||||
|
||||
var generalFolder models.Dashboard
|
||||
_, err = x.Table(&models.Dashboard{}).Where("title = ? AND org_id = ?", ualert.GENERAL_FOLDER, o.Id).Get(&generalFolder)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, generalFolder)
|
||||
|
||||
for _, rule := range rules {
|
||||
var expectedFolder models.Dashboard
|
||||
if rule.Title == a1.Name {
|
||||
expectedFolder = *folder1
|
||||
} else {
|
||||
expectedFolder = generalFolder
|
||||
}
|
||||
require.Equal(t, expectedFolder.Uid, rule.NamespaceUID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// setupTestDB prepares the sqlite database and runs OSS migrations to initialize the schemas.
|
||||
func setupTestDB(t *testing.T) *xorm.Engine {
|
||||
t.Helper()
|
||||
@@ -593,6 +637,18 @@ func teardown(t *testing.T, x *xorm.Engine) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func runDashAlertMigrationTestRun(t *testing.T, x *xorm.Engine) {
|
||||
_, 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)
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -637,6 +693,14 @@ func setupLegacyAlertsTables(t *testing.T, x *xorm.Engine, legacyChannels []*mod
|
||||
}
|
||||
}
|
||||
|
||||
func getAlertRules(t *testing.T, x *xorm.Engine, orgId int64) []*ngModels.AlertRule {
|
||||
rules := make([]*ngModels.AlertRule, 0)
|
||||
err := x.Table("alert_rule").Where("org_id = ?", orgId).Find(&rules)
|
||||
require.NoError(t, err)
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
// 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 := ""
|
||||
|
||||
@@ -248,11 +248,31 @@ func (m *migration) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
|
||||
|
||||
// cache for folders created for dashboards that have custom permissions
|
||||
folderCache := make(map[string]*dashboard)
|
||||
|
||||
gf := func(dash dashboard, da dashAlert) (*dashboard, error) {
|
||||
f, ok := folderCache[GENERAL_FOLDER]
|
||||
if !ok {
|
||||
// get or create general folder
|
||||
f, err = m.getOrCreateGeneralFolder(dash.OrgId)
|
||||
if err != nil {
|
||||
return nil, MigrationError{
|
||||
Err: fmt.Errorf("failed to get or create general folder under organisation %d: %w", dash.OrgId, err),
|
||||
AlertId: da.Id,
|
||||
}
|
||||
}
|
||||
folderCache[GENERAL_FOLDER] = f
|
||||
}
|
||||
// No need to assign default permissions to general folder
|
||||
// because they are included to the query result if it's a folder with no permissions
|
||||
// https://github.com/grafana/grafana/blob/076e2ce06a6ecf15804423fcc8dca1b620a321e5/pkg/services/sqlstore/dashboard_acl.go#L109
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Store of newly created rules to later create routes
|
||||
rulesPerOrg := make(map[int64]map[string]dashAlert)
|
||||
|
||||
for _, da := range dashAlerts {
|
||||
l := mg.Logger.New("ruleID", da.Id, "ruleName", da.Name, "dashboardUID", da.DashboardUID, "orgID", da.OrgId)
|
||||
newCond, err := transConditions(*da.ParsedSettings, da.OrgId, dsIDMap)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -282,7 +302,7 @@ func (m *migration) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
|
||||
folderName := getAlertFolderNameFromDashboard(&dash)
|
||||
f, ok := folderCache[folderName]
|
||||
if !ok {
|
||||
mg.Logger.Info("create a new folder for alerts that belongs to dashboard because it has custom permissions", "org", dash.OrgId, "dashboard_uid", dash.Uid, "folder", folderName)
|
||||
l.Info("create a new folder for alerts that belongs to dashboard because it has custom permissions", "folder", folderName)
|
||||
// create folder and assign the permissions of the dashboard (included default and inherited)
|
||||
f, err = m.createFolder(dash.OrgId, folderName)
|
||||
if err != nil {
|
||||
@@ -312,29 +332,20 @@ func (m *migration) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
|
||||
// get folder if exists
|
||||
f, err := m.getFolder(dash, da)
|
||||
if err != nil {
|
||||
return MigrationError{
|
||||
Err: err,
|
||||
AlertId: da.Id,
|
||||
}
|
||||
}
|
||||
folder = &f
|
||||
default:
|
||||
f, ok := folderCache[GENERAL_FOLDER]
|
||||
if !ok {
|
||||
// get or create general folder
|
||||
f, err = m.getOrCreateGeneralFolder(dash.OrgId)
|
||||
// If folder does not exist then the dashboard is an orphan and we migrate the alert to the general folder.
|
||||
l.Warn("Failed to find folder for dashboard. Migrate rule to the default folder", "rule_name", da.Name, "dashboard_uid", da.DashboardUID, "missing_folder_id", dash.FolderId)
|
||||
folder, err = gf(dash, da)
|
||||
if err != nil {
|
||||
return MigrationError{
|
||||
Err: fmt.Errorf("failed to get or create general folder under organisation %d: %w", dash.OrgId, err),
|
||||
AlertId: da.Id,
|
||||
}
|
||||
return err
|
||||
}
|
||||
folderCache[GENERAL_FOLDER] = f
|
||||
} else {
|
||||
folder = &f
|
||||
}
|
||||
default:
|
||||
folder, err = gf(dash, da)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// No need to assign default permissions to general folder
|
||||
// because they are included to the query result if it's a folder with no permissions
|
||||
// https://github.com/grafana/grafana/blob/076e2ce06a6ecf15804423fcc8dca1b620a321e5/pkg/services/sqlstore/dashboard_acl.go#L109
|
||||
folder = f
|
||||
}
|
||||
|
||||
if folder.Uid == "" {
|
||||
|
||||
Reference in New Issue
Block a user