Alerting: Time interval Delete API to check for usages in alert rules (#90500)
* Check if a time interval is used in alert rules before deleting it * Add time interval to parameters of ListAlertRulesQuery and ListNotificationSettings of DbStore == Refacorings == * refactor isMuteTimeInUse to accept a single route * update getMuteTiming to not return err * update delete to get the mute timing from config first
This commit is contained in:
@@ -240,6 +240,17 @@ type apiClient struct {
|
||||
url string
|
||||
}
|
||||
|
||||
type LegacyApiClient struct {
|
||||
apiClient
|
||||
}
|
||||
|
||||
func NewAlertingLegacyAPIClient(host, user, pass string) LegacyApiClient {
|
||||
cli := newAlertingApiClient(host, user, pass)
|
||||
return LegacyApiClient{
|
||||
apiClient: cli,
|
||||
}
|
||||
}
|
||||
|
||||
func newAlertingApiClient(host, user, pass string) apiClient {
|
||||
if len(user) == 0 && len(pass) == 0 {
|
||||
return apiClient{url: fmt.Sprintf("http://%s", host)}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"template_files": {},
|
||||
"template_file_provenances": {},
|
||||
"alertmanager_config": {
|
||||
"route": {
|
||||
"receiver": "grafana-default-email",
|
||||
"routes": [
|
||||
{
|
||||
"object_matchers": [
|
||||
[
|
||||
"test",
|
||||
"=",
|
||||
"test"
|
||||
]
|
||||
],
|
||||
"mute_time_intervals": [
|
||||
"test-interval"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"mute_time_intervals": [
|
||||
{
|
||||
"name": "test-interval",
|
||||
"time_intervals": [
|
||||
{
|
||||
"start_time": "06:00",
|
||||
"end_time": "23:59"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"receivers": [
|
||||
{
|
||||
"name": "grafana-default-email",
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"type": "email",
|
||||
"settings": {
|
||||
"addresses": "<example@email.com>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "Group1",
|
||||
"interval": "1m",
|
||||
"rules": [
|
||||
{
|
||||
"for": "0",
|
||||
"labels": {
|
||||
"label1": "test-label"
|
||||
},
|
||||
"annotations": {
|
||||
"annotation": "test-annotation"
|
||||
},
|
||||
"grafana_alert": {
|
||||
"title": "Rule1",
|
||||
"condition": "A",
|
||||
"data": [
|
||||
{
|
||||
"refId": "A",
|
||||
"datasourceUid": "__expr__",
|
||||
"model": {
|
||||
"expression": "0 > 0",
|
||||
"type": "math"
|
||||
}
|
||||
}
|
||||
],
|
||||
"no_data_state": "NoData",
|
||||
"exec_err_state": "Alerting",
|
||||
"notification_settings": {
|
||||
"receiver": "grafana-default-email",
|
||||
"mute_time_intervals": [
|
||||
"test-interval"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,8 +2,11 @@ package timeinterval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/alertmanager/config"
|
||||
@@ -24,12 +27,16 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/tests/api/alerting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
//go:embed test-data/*.*
|
||||
var testData embed.FS
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
@@ -626,3 +633,58 @@ func TestIntegrationTimeIntervalListSelector(t *testing.T) {
|
||||
require.Empty(t, list.Items)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
helper := getTestHelper(t)
|
||||
|
||||
cliCfg := helper.Org1.Admin.NewRestConfig()
|
||||
legacyCli := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
|
||||
|
||||
// Prepare environment and create notification policy and rule that use time interval
|
||||
alertmanagerRaw, err := testData.ReadFile(path.Join("test-data", "notification-settings.json"))
|
||||
require.NoError(t, err)
|
||||
var amConfig definitions.PostableUserConfig
|
||||
require.NoError(t, json.Unmarshal(alertmanagerRaw, &amConfig))
|
||||
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
|
||||
postGroupRaw, err := testData.ReadFile(path.Join("test-data", "rulegroup-1.json"))
|
||||
require.NoError(t, err)
|
||||
var ruleGroup definitions.PostableRuleGroupConfig
|
||||
require.NoError(t, json.Unmarshal(postGroupRaw, &ruleGroup))
|
||||
|
||||
folderUID := "test-folder"
|
||||
legacyCli.CreateFolder(t, folderUID, "TEST")
|
||||
_, status, data := legacyCli.PostRulesGroupWithStatus(t, folderUID, &ruleGroup)
|
||||
require.Equalf(t, http.StatusAccepted, status, "Failed to post Rule: %s", data)
|
||||
|
||||
adminK8sClient, err := versioned.NewForConfig(cliCfg)
|
||||
require.NoError(t, err)
|
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
|
||||
|
||||
intervals, err := adminClient.List(ctx, v1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, intervals.Items, 1)
|
||||
|
||||
intervalToDelete := intervals.Items[0]
|
||||
|
||||
t.Run("should fail to delete if time interval is used in rule and routes", func(t *testing.T) {
|
||||
err := adminClient.Delete(ctx, intervalToDelete.Name, v1.DeleteOptions{})
|
||||
require.Truef(t, errors.IsConflict(err), "Expected Conflict, got: %s", err)
|
||||
})
|
||||
|
||||
t.Run("should fail to delete if time interval is used in only rule", func(t *testing.T) {
|
||||
amConfig.AlertmanagerConfig.Route.Routes[0].MuteTimeIntervals = nil
|
||||
success, err := legacyCli.PostConfiguration(t, amConfig)
|
||||
require.Truef(t, success, "Failed to post Alertmanager configuration: %s", err)
|
||||
|
||||
err = adminClient.Delete(ctx, intervalToDelete.Name, v1.DeleteOptions{})
|
||||
require.Truef(t, errors.IsConflict(err), "Expected Conflict, got: %s", err)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user