Alerting: Remove ngalert feature toggle and introduce two new settings for enabling Grafana 8 alerts and disabling them for specific organisations (#38746)

* Remove `ngalert` feature toggle

* Update frontend

Remove all references of ngalert feature toggle

* Update docs

* Disable unified alerting for specific orgs

* Add backend tests

* Apply suggestions from code review

Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>

* Disabled unified alerting by default

* Ensure backward compatibility with old ngalert feature toggle

* Apply suggestions from code review

Co-authored-by: gotjosh <josue@grafana.com>
This commit is contained in:
Sofia Papagiannaki
2021-09-29 16:16:40 +02:00
committed by GitHub
co-authored by achatterjee-grafana gotjosh
parent 2dedbcd3c3
commit 012d4f0905
57 changed files with 705 additions and 183 deletions
+17 -11
View File
@@ -427,11 +427,6 @@ func (cfg Cfg) IsLiveConfigEnabled() bool {
return cfg.FeatureToggles["live-config"]
}
// IsNgAlertEnabled returns whether the standalone alerts feature is enabled.
func (cfg Cfg) IsNgAlertEnabled() bool {
return cfg.FeatureToggles["ngalert"]
}
// IsTrimDefaultsEnabled returns whether the standalone trim dashboard default feature is enabled.
func (cfg Cfg) IsTrimDefaultsEnabled() bool {
return cfg.FeatureToggles["trimDefaults"]
@@ -938,12 +933,12 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
cfg.PluginAdminEnabled = pluginsSection.Key("plugin_admin_enabled").MustBool(false)
cfg.PluginAdminExternalManageEnabled = pluginsSection.Key("plugin_admin_external_manage_enabled").MustBool(false)
// Read and populate feature toggles list
featureTogglesSection := iniFile.Section("feature_toggles")
cfg.FeatureToggles = make(map[string]bool)
featuresTogglesStr := valueAsString(featureTogglesSection, "enable", "")
for _, feature := range util.SplitString(featuresTogglesStr) {
cfg.FeatureToggles[feature] = true
if err := cfg.readFeatureToggles(iniFile); err != nil {
return err
}
if err := cfg.ReadUnifiedAlertingSettings(iniFile); err != nil {
return err
}
// check old location for this option
@@ -1372,6 +1367,17 @@ func (cfg *Cfg) readRenderingSettings(iniFile *ini.File) error {
return nil
}
func (cfg *Cfg) readFeatureToggles(iniFile *ini.File) error {
// Read and populate feature toggles list
featureTogglesSection := iniFile.Section("feature_toggles")
cfg.FeatureToggles = make(map[string]bool)
featuresTogglesStr := valueAsString(featureTogglesSection, "enable", "")
for _, feature := range util.SplitString(featuresTogglesStr) {
cfg.FeatureToggles[feature] = true
}
return nil
}
func readAlertingSettings(iniFile *ini.File) error {
alerting := iniFile.Section("alerting")
AlertingEnabled = alerting.Key("enabled").MustBool(true)
+1 -1
View File
@@ -68,7 +68,7 @@ func (cfg *Cfg) readQuotaSettings() {
var alertOrgQuota int64
var alertGlobalQuota int64
if cfg.IsNgAlertEnabled() {
if cfg.UnifiedAlerting.Enabled {
alertOrgQuota = quota.Key("org_alert_rule").MustInt64(100)
alertGlobalQuota = quota.Key("global_alert_rule").MustInt64(-1)
}
+251
View File
@@ -11,6 +11,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/ini.v1"
@@ -427,3 +428,253 @@ func TestGetCDNPathWithAlphaVersion(t *testing.T) {
require.Equal(t, "http://cdn.grafana.com/grafana-oss/v7.5.0-alpha.11124/", cfg.GetContentDeliveryURL("grafana-oss"))
require.Equal(t, "http://cdn.grafana.com/grafana/v7.5.0-alpha.11124/", cfg.GetContentDeliveryURL("grafana"))
}
func TestAlertingEnabled(t *testing.T) {
testCases := []struct {
desc string
unifiedAlertingEnabled string
legacyAlertingEnabled string
featureToggleSet bool
verifyCfg func(*testing.T, Cfg, *ini.File)
}{
{
desc: "when legacy alerting is enabled and unified is disabled",
legacyAlertingEnabled: "true",
unifiedAlertingEnabled: "false",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, false)
assert.Equal(t, AlertingEnabled, true)
},
},
{
desc: "when legacy alerting is disabled and unified is enabled",
legacyAlertingEnabled: "false",
unifiedAlertingEnabled: "true",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when both alerting are enabled, it should error",
legacyAlertingEnabled: "true",
unifiedAlertingEnabled: "true",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.Error(t, err)
},
},
{
desc: "when legacy alerting is invalid and unified is disabled",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "false",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, false)
assert.Equal(t, AlertingEnabled, true)
},
},
{
desc: "when legacy alerting is invalid and unified is enabled",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "true",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.Error(t, err)
},
},
{
desc: "when legacy alerting is enabled and unified is invalid",
legacyAlertingEnabled: "true",
unifiedAlertingEnabled: "invalid",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, false)
assert.Equal(t, AlertingEnabled, true)
},
},
{
desc: "when legacy alerting is disabled and unified is invalid",
legacyAlertingEnabled: "false",
unifiedAlertingEnabled: "invalid",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, false)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when both are invalid",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "invalid",
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, false)
assert.Equal(t, AlertingEnabled, true)
},
},
{
desc: "when legacy alerting is enabled and unified is disabled and feature toggle is set",
legacyAlertingEnabled: "true",
unifiedAlertingEnabled: "false",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when legacy alerting is disabled and unified is disabled and feature toggle is set",
legacyAlertingEnabled: "false",
unifiedAlertingEnabled: "false",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when legacy alerting is disabled and unified is invalid and feature toggle is set",
legacyAlertingEnabled: "false",
unifiedAlertingEnabled: "invalid",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when legacy alerting is invalid and unified is disabled and feature toggle is set",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "false",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
{
desc: "when legacy alerting is invalid and unified is enabled and feature toggle is set",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "true",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.Error(t, err)
},
},
{
desc: "when both are invalid and feature toggle is set",
legacyAlertingEnabled: "invalid",
unifiedAlertingEnabled: "invalid",
featureToggleSet: true,
verifyCfg: func(t *testing.T, cfg Cfg, f *ini.File) {
err := readAlertingSettings(f)
require.NoError(t, err)
err = cfg.readFeatureToggles(f)
require.NoError(t, err)
err = cfg.ReadUnifiedAlertingSettings(f)
require.NoError(t, err)
assert.Equal(t, cfg.UnifiedAlerting.Enabled, true)
assert.Equal(t, AlertingEnabled, false)
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
t.Cleanup(func() {
AlertingEnabled = false
})
f := ini.Empty()
cfg := NewCfg()
unifiedAlertingSec, err := f.NewSection("unified_alerting")
require.NoError(t, err)
_, err = unifiedAlertingSec.NewKey("enabled", tc.unifiedAlertingEnabled)
require.NoError(t, err)
alertingSec, err := f.NewSection("alerting")
require.NoError(t, err)
_, err = alertingSec.NewKey("enabled", tc.legacyAlertingEnabled)
require.NoError(t, err)
if tc.featureToggleSet {
alertingSec, err := f.NewSection("feature_toggles")
require.NoError(t, err)
_, err = alertingSec.NewKey("enable", "ngalert")
require.NoError(t, err)
}
tc.verifyCfg(t, *cfg, f)
})
}
}
+28
View File
@@ -1,10 +1,13 @@
package setting
import (
"errors"
"strconv"
"strings"
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/grafana/grafana/pkg/util"
"github.com/prometheus/alertmanager/cluster"
"gopkg.in/ini.v1"
@@ -60,6 +63,8 @@ type UnifiedAlertingSettings struct {
EvaluationTimeout time.Duration
ExecuteAlerts bool
DefaultConfiguration string
Enabled bool
DisabledOrgs map[int64]struct{}
}
// ReadUnifiedAlertingSettings reads both the `unified_alerting` and `alerting` sections of the configuration while preferring configuration the `alerting` section.
@@ -67,6 +72,29 @@ type UnifiedAlertingSettings struct {
func (cfg *Cfg) ReadUnifiedAlertingSettings(iniFile *ini.File) error {
uaCfg := UnifiedAlertingSettings{}
ua := iniFile.Section("unified_alerting")
uaCfg.Enabled = ua.Key("enabled").MustBool(false)
// TODO: Deprecate this in v8.4, if the old feature toggle ngalert is set, enable Grafana 8 Unified Alerting anyway.
if !uaCfg.Enabled && cfg.FeatureToggles["ngalert"] {
cfg.Logger.Warn("ngalert feature flag is deprecated: use unified alerting enabled setting instead")
uaCfg.Enabled = true
AlertingEnabled = false
}
if uaCfg.Enabled && AlertingEnabled {
return errors.New("both legacy and Grafana 8 Alerts are enabled")
}
uaCfg.DisabledOrgs = make(map[int64]struct{})
orgsStr := valueAsString(ua, "disabled_orgs", "")
for _, org := range util.SplitString(orgsStr) {
orgID, err := strconv.ParseInt(org, 10, 64)
if err != nil {
return err
}
uaCfg.DisabledOrgs[orgID] = struct{}{}
}
var err error
uaCfg.AdminConfigPollInterval, err = gtime.ParseDuration(valueAsString(ua, "admin_config_poll_interval", (schedulerDefaultAdminConfigPollInterval).String()))
if err != nil {