Alerting: Add clean_upgrade config and deprecate force_migration (#78324)

* Alerting: Add clean_upgrade config and deprecate force_migration

Upgrading to UA and rolling back will no longer delete any data by default. 
Instead, each set of tables will remain unchanged when switching between 
legacy and UA. As such, the force_migration config has been deprecated 
and no extra configuration is required to roll back to legacy anymore.

If clean_upgrade is set to true when upgrading from legacy alerting to Unified
Alerting, grafana will first delete all existing Unified Alerting resources,
thus re-upgrading all organizations from scratch. If false or unset,
organizations that have previously upgraded will not lose their existing Unified
 Alerting data when switching between legacy and Unified Alerting.

 Similar to force_migration, it should be kept false when not needed as it may
 cause unintended data-loss if left enabled.

---------

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
Matthew Jacobson
2023-11-30 11:01:11 -05:00
committed by GitHub
co-authored by Christopher Moyer
parent 0f0249abea
commit 5a80962de9
9 changed files with 144 additions and 47 deletions
@@ -50,27 +50,30 @@ func TestServiceStart(t *testing.T) {
expected: migrationStore.UnifiedAlerting,
},
{
name: "when unified alerting disabled, migration is already run and force migration is enabled, then revert migration",
name: "when unified alerting disabled, migration is already run and CleanUpgrade is enabled, then revert migration",
config: &setting.Cfg{
UnifiedAlerting: setting.UnifiedAlertingSettings{
Enabled: pointer(false),
Upgrade: setting.UnifiedAlertingUpgradeSettings{
CleanUpgrade: true,
},
},
ForceMigration: true,
},
starting: migrationStore.UnifiedAlerting,
expected: migrationStore.Legacy,
},
{
name: "when unified alerting disabled, migration is already run and force migration is disabled, then the migration should panic",
name: "when unified alerting disabled, migration is already run and CleanUpgrade is disabled, then the migration status should set to false",
config: &setting.Cfg{
UnifiedAlerting: setting.UnifiedAlertingSettings{
Enabled: pointer(false),
Upgrade: setting.UnifiedAlertingUpgradeSettings{
CleanUpgrade: false,
},
},
ForceMigration: false,
},
starting: migrationStore.UnifiedAlerting,
expected: migrationStore.UnifiedAlerting,
expectedErr: true,
starting: migrationStore.UnifiedAlerting,
expected: migrationStore.Legacy,
},
{
name: "when unified alerting enabled and migration is already run, then do nothing",
@@ -92,6 +95,28 @@ func TestServiceStart(t *testing.T) {
starting: migrationStore.Legacy,
expected: migrationStore.Legacy,
},
{
name: "when unified alerting disabled, migration is already run and force migration is enabled, then revert migration",
config: &setting.Cfg{
UnifiedAlerting: setting.UnifiedAlertingSettings{
Enabled: pointer(false),
},
ForceMigration: true,
},
starting: migrationStore.UnifiedAlerting,
expected: migrationStore.Legacy,
},
{
name: "when unified alerting disabled, migration is already run and force migration is disabled, then the migration status should set to false",
config: &setting.Cfg{
UnifiedAlerting: setting.UnifiedAlertingSettings{
Enabled: pointer(false),
},
ForceMigration: false,
},
starting: migrationStore.UnifiedAlerting,
expected: migrationStore.Legacy,
},
}
sqlStore := db.InitTestDB(t)
+7 -11
View File
@@ -16,9 +16,6 @@ import (
// actionName is the unique row-level lock name for serverlock.ServerLockService.
const actionName = "alerting migration"
//nolint:stylecheck
var ForceMigrationError = fmt.Errorf("Grafana has already been migrated to Unified Alerting. Any alert rules created while using Unified Alerting will be deleted by rolling back. Set force_migration=true in your grafana.ini and restart Grafana to roll back and delete Unified Alerting configuration data.")
type UpgradeService interface {
Run(ctx context.Context) error
}
@@ -84,6 +81,7 @@ func newTransition(currentType migrationStore.AlertingType, cfg *setting.Cfg) tr
CurrentType: currentType,
DesiredType: desiredType,
CleanOnDowngrade: cfg.ForceMigration,
CleanOnUpgrade: cfg.UnifiedAlerting.Upgrade.CleanUpgrade,
}
}
@@ -92,6 +90,7 @@ type transition struct {
CurrentType migrationStore.AlertingType
DesiredType migrationStore.AlertingType
CleanOnDowngrade bool
CleanOnUpgrade bool
}
// isNoChange returns true if the migration is a no-op.
@@ -111,30 +110,27 @@ func (t transition) isDowngrading() bool {
// shouldClean returns true if the migration should delete all unified alerting data.
func (t transition) shouldClean() bool {
return t.isDowngrading() && t.CleanOnDowngrade
return t.isDowngrading() && t.CleanOnDowngrade || t.isUpgrading() && t.CleanOnUpgrade
}
// applyTransition applies the transition to the database.
// If the transition is a no-op, nothing will be done.
// If the transition is a downgrade and CleanOnDowngrade is false, nothing will be done.
// If the transition is a downgrade and CleanOnDowngrade is true, all unified alerting data will be deleted.
// If the transition is a downgrade and CleanOnDowngrade is false, an error will be returned.
// If the transition is an upgrade, all orgs will be migrated.
// If the transition is an upgrade and CleanOnUpgrade is false, all orgs will be migrated.
// If the transition is an upgrade and CleanOnUpgrade is true, all unified alerting data will be deleted and then all orgs will be migrated.
func (ms *migrationService) applyTransition(ctx context.Context, t transition) error {
l := ms.log.New(
"CurrentType", t.CurrentType,
"DesiredType", t.DesiredType,
"CleanOnDowngrade", t.CleanOnDowngrade,
"CleanOnUpgrade", t.CleanOnUpgrade,
)
if t.isNoChange() {
l.Info("Migration already complete")
return nil
}
// Safeguard to prevent accidental data loss when reverting from UA to LA.
if t.isDowngrading() && !ms.cfg.ForceMigration {
return ForceMigrationError
}
if t.shouldClean() {
l.Info("Cleaning up unified alerting data")
if err := ms.migrationStore.RevertAllOrgs(ctx); err != nil {
+38 -19
View File
@@ -197,9 +197,9 @@ func TestServiceRevert(t *testing.T) {
// Run migration.
ctx := context.Background()
cfg := &setting.Cfg{
ForceMigration: true,
UnifiedAlerting: setting.UnifiedAlertingSettings{
Enabled: pointer(true),
Upgrade: setting.UnifiedAlertingUpgradeSettings{},
},
}
service := NewTestMigrationService(t, sqlStore, cfg)
@@ -280,7 +280,7 @@ func TestServiceRevert(t *testing.T) {
}
})
t.Run("ForceMigration story", func(t *testing.T) {
t.Run("CleanUpgrade story", func(t *testing.T) {
sqlStore := db.InitTestDB(t)
x := sqlStore.GetEngine()
@@ -304,14 +304,45 @@ func TestServiceRevert(t *testing.T) {
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 1)
// Disable UA without ForceMigration.
// This run should throw an error.
// Disable UA.
// This run should just set migration status to false.
service.cfg.UnifiedAlerting.Enabled = pointer(false)
require.ErrorContains(t, service.Run(ctx), ForceMigrationError.Error())
checkAlertingType(t, ctx, service, migrationStore.UnifiedAlerting)
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.Legacy)
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 1)
// Add another alert.
// Enable UA without clean flag.
// This run should not remigrate org, new alert is not migrated.
_, alertErr := x.Insert(createAlert(t, 1, 1, 2, "alert2", []string{"notifier1"}))
require.NoError(t, alertErr)
service.cfg.UnifiedAlerting.Enabled = pointer(true)
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.UnifiedAlerting)
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 1) // Still 1
// Disable UA with clean flag.
// This run should not revert UA data.
service.cfg.UnifiedAlerting.Enabled = pointer(false)
service.cfg.UnifiedAlerting.Upgrade.CleanUpgrade = true
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.Legacy)
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 1) // Still 1
// Enable UA with clean flag.
// This run should revert and remigrate org, new alert is migrated.
service.cfg.UnifiedAlerting.Enabled = pointer(true)
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.UnifiedAlerting)
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 2) // Now we have 2
// The following tests ForceMigration which is deprecated and will be removed in v11.
service.cfg.UnifiedAlerting.Upgrade.CleanUpgrade = false
// Disable UA with force flag.
// This run should not revert UA data.
service.cfg.UnifiedAlerting.Enabled = pointer(false)
@@ -319,19 +350,7 @@ func TestServiceRevert(t *testing.T) {
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.Legacy)
checkMigrationStatus(t, ctx, service, 1, false)
checkAlertRulesCount(t, x, 1, 0) // Alerts are gone.
// Add another alert.
_, alertErr := x.Insert(createAlert(t, 1, 1, 2, "alert2", []string{"notifier1"}))
require.NoError(t, alertErr)
// Enable UA.
// This run should remigrate org, new alert is migrated.
service.cfg.UnifiedAlerting.Enabled = pointer(true)
require.NoError(t, service.Run(ctx))
checkAlertingType(t, ctx, service, migrationStore.UnifiedAlerting)
checkMigrationStatus(t, ctx, service, 1, true)
checkAlertRulesCount(t, x, 1, 2) // Now we have 2
checkAlertRulesCount(t, x, 1, 0)
})
}