From dc3e17ba5b0ace7df423ad5eae85d9da3d29c644 Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Tue, 11 May 2021 08:08:39 -0400 Subject: [PATCH] Alerting: Change rule migration to be based on feature flag (#33792) makes it so the feature flag can be turned on off, and the migration will be cleared and rerun. All existing NG alert rules, configuration settings, etc are removed when disabling the feature flag. for https://github.com/grafana/alerting-squad/issues/142 Co-authored-by: Sofia Papagiannaki --- .../sqlstore/migrations/migrations_test.go | 5 +- .../sqlstore/migrations/ualert/permissions.go | 5 +- .../sqlstore/migrations/ualert/ualert.go | 76 ++++++++++++++++++- pkg/services/sqlstore/migrator/migrator.go | 15 +++- pkg/services/sqlstore/sqlstore.go | 2 +- 5 files changed, 95 insertions(+), 8 deletions(-) diff --git a/pkg/services/sqlstore/migrations/migrations_test.go b/pkg/services/sqlstore/migrations/migrations_test.go index 0bf54752876..23765632f0d 100644 --- a/pkg/services/sqlstore/migrations/migrations_test.go +++ b/pkg/services/sqlstore/migrations/migrations_test.go @@ -5,6 +5,7 @@ import ( . "github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil" + "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/require" "xorm.io/xorm" ) @@ -23,7 +24,7 @@ func TestMigrations(t *testing.T) { _, err = x.SQL(query).Get(&result) require.Error(t, err) - mg := NewMigrator(x) + mg := NewMigrator(x, &setting.Cfg{}) AddMigrations(mg) expectedMigrations := mg.MigrationsCount() @@ -36,7 +37,7 @@ func TestMigrations(t *testing.T) { require.Equal(t, expectedMigrations, result.Count) - mg = NewMigrator(x) + mg = NewMigrator(x, &setting.Cfg{}) AddMigrations(mg) err = mg.Start() diff --git a/pkg/services/sqlstore/migrations/ualert/permissions.go b/pkg/services/sqlstore/migrations/ualert/permissions.go index 19ae39af805..0110c91a8f8 100644 --- a/pkg/services/sqlstore/migrations/ualert/permissions.go +++ b/pkg/services/sqlstore/migrations/ualert/permissions.go @@ -44,7 +44,6 @@ func (m *migration) createFolder(orgID int64, title string) (*dashboard, error) }), } dash := cmd.getDashboardModel() - var userId int64 = -1 uid, err := m.generateNewDashboardUid(dash.OrgId) if err != nil { @@ -55,9 +54,9 @@ func (m *migration) createFolder(orgID int64, title string) (*dashboard, error) parentVersion := dash.Version dash.setVersion(1) dash.Created = time.Now() - dash.CreatedBy = userId + dash.CreatedBy = FOLDER_CREATED_BY dash.Updated = time.Now() - dash.UpdatedBy = userId + dash.UpdatedBy = FOLDER_CREATED_BY metrics.MApiDashboardInsert.Inc() if _, err = m.sess.Insert(dash); err != nil { diff --git a/pkg/services/sqlstore/migrations/ualert/ualert.go b/pkg/services/sqlstore/migrations/ualert/ualert.go index cb267c4f773..c765ff2d28a 100644 --- a/pkg/services/sqlstore/migrations/ualert/ualert.go +++ b/pkg/services/sqlstore/migrations/ualert/ualert.go @@ -11,6 +11,14 @@ import ( const GENERAL_FOLDER = "General Alerting" const DASHBOARD_FOLDER = "Migrated %s" +// FOLDER_CREATED_BY us used to track folders created by this migration +// during alert migration cleanup. +const FOLDER_CREATED_BY = -8 + +var migTitle = "move dashboard alerts to unified alerting" + +var rmMigTitle = "remove unified alerting data" + type MigrationError struct { AlertId int64 Err error @@ -26,7 +34,31 @@ func AddMigration(mg *migrator.Migrator) { if os.Getenv("UALERT_MIG") == "iDidBackup" { // TODO: unified alerting DB needs to be extacted into ../migrations.go // so it runs and creates the tables before this migration runs. - mg.AddMigration("move dashboard alerts to unified alerting", &migration{}) + logs, err := mg.GetMigrationLog() + if err != nil { + mg.Logger.Crit("alert migration failure: could not get migration log", "error", err) + os.Exit(1) + } + + _, migrationRun := logs[migTitle] + + ngEnabled := mg.Cfg.IsNgAlertEnabled() + + switch { + case ngEnabled && !migrationRun: + // clear the entry of the migration that + err = mg.ClearMigrationEntry(rmMigTitle) + if err != nil { + mg.Logger.Error("alert migration error: could not clear alert migration for removing data", "error", err) + } + mg.AddMigration(migTitle, &migration{}) + case !ngEnabled && migrationRun: + err = mg.ClearMigrationEntry(migTitle) + if err != nil { + mg.Logger.Error("alert migration error: could not clear alert migration", "error", err) + } + mg.AddMigration(rmMigTitle, &rmMigration{}) + } } } @@ -184,3 +216,45 @@ func (m *migration) Exec(sess *xorm.Session, mg *migrator.Migrator) error { return nil } + +type rmMigration struct { + migrator.MigrationBase +} + +func (m *rmMigration) SQL(dialect migrator.Dialect) string { + return "code migration" +} + +func (m *rmMigration) Exec(sess *xorm.Session, mg *migrator.Migrator) error { + _, err := sess.Exec("delete from alert_rule") + if err != nil { + return err + } + + _, err = sess.Exec("delete from alert_rule_version") + if err != nil { + return err + } + + _, err = sess.Exec("delete from dashboard_acl where dashboard_id IN (select id from dashboard where created_by = ?)", FOLDER_CREATED_BY) + if err != nil { + return err + } + + _, err = sess.Exec("delete from dashboard where created_by = ?", FOLDER_CREATED_BY) + if err != nil { + return err + } + + _, err = sess.Exec("delete from alert_configuration") + if err != nil { + return err + } + + _, err = sess.Exec("delete from alert_instance") + if err != nil { + return err + } + + return nil +} diff --git a/pkg/services/sqlstore/migrator/migrator.go b/pkg/services/sqlstore/migrator/migrator.go index f05a87c2af8..b40d7e0468b 100644 --- a/pkg/services/sqlstore/migrator/migrator.go +++ b/pkg/services/sqlstore/migrator/migrator.go @@ -6,6 +6,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/errutil" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" @@ -17,6 +18,7 @@ type Migrator struct { Dialect Dialect migrations []Migration Logger log.Logger + Cfg *setting.Cfg } type MigrationLog struct { @@ -28,12 +30,13 @@ type MigrationLog struct { Timestamp time.Time } -func NewMigrator(engine *xorm.Engine) *Migrator { +func NewMigrator(engine *xorm.Engine, cfg *setting.Cfg) *Migrator { mg := &Migrator{} mg.x = engine mg.Logger = log.New("migrator") mg.migrations = make([]Migration, 0) mg.Dialect = NewDialect(mg.x) + mg.Cfg = cfg return mg } @@ -168,6 +171,16 @@ func (mg *Migrator) exec(m Migration, sess *xorm.Session) error { return nil } +func (mg *Migrator) ClearMigrationEntry(id string) error { + sess := mg.x.NewSession() + defer sess.Close() + _, err := sess.SQL(`DELETE from migration_log where migration_id = ?`, id).Query() + if err != nil { + return fmt.Errorf("failed to clear migration entry %v: %w", id, err) + } + return nil +} + type dbTransactionFunc func(sess *xorm.Session) error func (mg *Migrator) inTransaction(callback dbTransactionFunc) error { diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 05ec397bf27..457e83551f9 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -85,7 +85,7 @@ func (ss *SQLStore) Init() error { dialect = ss.Dialect if !ss.dbCfg.SkipMigrations { - migrator := migrator.NewMigrator(ss.engine) + migrator := migrator.NewMigrator(ss.engine, ss.Cfg) migrations.AddMigrations(migrator) for _, descriptor := range registry.GetServices() {