Alerting: Fix rule API to accept 0 duration of field For (#50992)
* make 'for' pointer to distinguish between missing field and 0 * set 'for' to -1 if the value is missing but not allow negative in the request + path -1 with the value from original rule * update store validation to not allow negative 'for' * update usages to use pointer
This commit is contained in:
@@ -161,7 +161,7 @@ func TestAdminConfiguration_SendingToExternalAlertmanagers(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
|
||||
@@ -624,7 +624,7 @@ func TestRulerAccess(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -865,7 +865,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: "arulegroup",
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -881,7 +881,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: "arulegroup",
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -911,7 +911,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: "arulegroup",
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -941,7 +941,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: getLongString(t, ngstore.AlertRuleMaxTitleLength+1),
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -972,7 +972,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
interval: invalidInterval,
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -1002,7 +1002,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: "arulegroup",
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -1032,7 +1032,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
rulegroup: "arulegroup",
|
||||
rule: apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -1090,7 +1090,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -1219,6 +1219,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"expr":"",
|
||||
"for": "0s",
|
||||
"grafana_alert":{
|
||||
"id":2,
|
||||
"orgId":1,
|
||||
@@ -1269,7 +1270,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{
|
||||
"label1": "val42",
|
||||
"foo": "bar",
|
||||
@@ -1342,7 +1343,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{
|
||||
"label1": "val42",
|
||||
"foo": "bar",
|
||||
@@ -1376,7 +1377,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
},
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{
|
||||
"label1": "val42",
|
||||
"foo": "bar",
|
||||
@@ -1448,7 +1449,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: forValue,
|
||||
For: &forValue,
|
||||
Labels: map[string]string{
|
||||
// delete foo label
|
||||
"label1": "val1", // update label value
|
||||
@@ -1575,7 +1576,7 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: forValue,
|
||||
For: &forValue,
|
||||
},
|
||||
GrafanaManagedAlert: &apimodels.PostableGrafanaRule{
|
||||
UID: ruleUID, // Including the UID in the payload makes the endpoint update the existing rule.
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestPrometheusRules(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -163,7 +163,7 @@ func TestPrometheusRules(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{"__panelId__": "1"},
|
||||
},
|
||||
@@ -351,7 +351,7 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{
|
||||
"__dashboardUid__": dashboardUID,
|
||||
|
||||
@@ -274,14 +274,14 @@ func createRule(t *testing.T, client apiClient, folder string) {
|
||||
|
||||
interval, err := model.ParseDuration("1m")
|
||||
require.NoError(t, err)
|
||||
|
||||
doubleInterval := 2 * interval
|
||||
rules := apimodels.PostableRuleGroupConfig{
|
||||
Name: "arulegroup",
|
||||
Interval: interval,
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: 2 * interval,
|
||||
For: &doubleInterval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -414,7 +414,7 @@ func TestRulerRulesFilterByDashboard(t *testing.T) {
|
||||
Rules: []apimodels.PostableExtendedRuleNode{
|
||||
{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{
|
||||
"__dashboardUid__": dashboardUID,
|
||||
@@ -513,6 +513,7 @@ func TestRulerRulesFilterByDashboard(t *testing.T) {
|
||||
}
|
||||
}, {
|
||||
"expr": "",
|
||||
"for":"0s",
|
||||
"grafana_alert": {
|
||||
"id": 2,
|
||||
"orgId": 1,
|
||||
@@ -814,13 +815,55 @@ func TestRuleGroupSequence(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRuleUpdate(t *testing.T) {
|
||||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableLegacyAlerting: true,
|
||||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
})
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, user.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Password: "password",
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
client := newAlertingApiClient(grafanaListedAddr, "grafana", "password")
|
||||
folder1Title := "folder1"
|
||||
client.CreateFolder(t, util.GenerateShortUID(), folder1Title)
|
||||
|
||||
t.Run("should be able to reset 'for' to 0", func(t *testing.T) {
|
||||
group := generateAlertRuleGroup(1, alertRuleGen())
|
||||
expected := model.Duration(10 * time.Second)
|
||||
group.Rules[0].ApiRuleNode.For = &expected
|
||||
|
||||
status, body := client.PostRulesGroup(t, folder1Title, &group)
|
||||
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
||||
getGroup := client.GetRulesGroup(t, folder1Title, group.Name)
|
||||
require.Equal(t, expected, *getGroup.Rules[0].ApiRuleNode.For)
|
||||
|
||||
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
|
||||
expected = 0
|
||||
group.Rules[0].ApiRuleNode.For = &expected
|
||||
status, body = client.PostRulesGroup(t, folder1Title, &group)
|
||||
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
|
||||
|
||||
getGroup = client.GetRulesGroup(t, folder1Title, group.Name)
|
||||
require.Equal(t, expected, *getGroup.Rules[0].ApiRuleNode.For)
|
||||
})
|
||||
}
|
||||
|
||||
func newTestingRuleConfig(t *testing.T) apimodels.PostableRuleGroupConfig {
|
||||
interval, err := model.ParseDuration("1m")
|
||||
require.NoError(t, err)
|
||||
|
||||
firstRule := apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
@@ -847,7 +890,7 @@ func newTestingRuleConfig(t *testing.T) apimodels.PostableRuleGroupConfig {
|
||||
}
|
||||
secondRule := apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: interval,
|
||||
For: &interval,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
|
||||
@@ -87,9 +87,10 @@ func getBody(t *testing.T, body io.ReadCloser) string {
|
||||
|
||||
func alertRuleGen() func() apimodels.PostableExtendedRuleNode {
|
||||
return func() apimodels.PostableExtendedRuleNode {
|
||||
forDuration := model.Duration(10 * time.Second)
|
||||
return apimodels.PostableExtendedRuleNode{
|
||||
ApiRuleNode: &apimodels.ApiRuleNode{
|
||||
For: model.Duration(10 * time.Second),
|
||||
For: &forDuration,
|
||||
Labels: map[string]string{"label1": "val1"},
|
||||
Annotations: map[string]string{"annotation1": "val1"},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user