Alerting: Add MissingSeriesEvalsToResolve to the APIs (#102150)

What is this feature?

A follow-up for #101184, adds AlertRule.MissingSeriesEvalsToResolve to the APIs.

missing_series_evals_to_resolve must be specified too and it must be > 0.

POST /api/ruler/grafana/api/v1/rules/{folderUID} works in the following way:

    If missing_series_evals_to_resolve is not sent or null, the rule keeps its existing value
    If missing_series_evals_to_resolve > 0: updates to that value
    If missing_series_evals_to_resolve = 0: resets to default (nil).
    AlertRule.MissingSeriesEvalsToResolve can't be 0, so I used it to reset

In the Provisioning API, the value is just set if present and > 0. Otherwise it's reset:

PUT to /api/v1/provisioning/alert-rules/{UID}:

    If missing_series_evals_to_resolve is nil, it's reset to the default value
    If missing_series_evals_to_resolve > 0, it's updated
This commit is contained in:
Alexander Akhmetov
2025-03-26 13:34:53 +01:00
committed by GitHub
parent f2fc1d8575
commit f49a88ab72
17 changed files with 462 additions and 116 deletions
+93 -4
View File
@@ -1753,6 +1753,94 @@ func TestIntegrationRuleUpdate(t *testing.T) {
require.Equal(t, newKeepFiringFor, *getGroup.Rules[0].ApiRuleNode.KeepFiringFor)
})
t.Run("missing_series_evals_to_resolve", func(t *testing.T) {
testCases := []struct {
name string
initialValue *int
updatedValue *int
expectedValue *int
expectedStatus int
}{
{
name: "should be able to set missing_series_evals_to_resolve to 5",
initialValue: nil,
updatedValue: util.Pointer(5),
expectedValue: util.Pointer(5),
expectedStatus: http.StatusAccepted,
},
{
name: "should be able to update missing_series_evals_to_resolve",
initialValue: util.Pointer(1),
updatedValue: util.Pointer(2),
expectedValue: util.Pointer(2),
expectedStatus: http.StatusAccepted,
},
{
name: "should preserve missing_series_evals_to_resolve when it's set nil",
initialValue: util.Pointer(5),
updatedValue: nil,
expectedValue: util.Pointer(5),
expectedStatus: http.StatusAccepted,
},
{
name: "should reject missing_series_evals_to_resolve < 0",
initialValue: util.Pointer(1),
updatedValue: util.Pointer(-1),
expectedStatus: http.StatusBadRequest,
},
{
name: "should be able to reset missing_series_evals_to_resolve by setting it to 0",
initialValue: util.Pointer(1),
updatedValue: util.Pointer(0),
expectedValue: nil,
expectedStatus: http.StatusAccepted,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create a new rule
group := generateAlertRuleGroup(1, alertRuleGen())
group.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve = tc.initialValue
// Post the rule group with our alert rule
_, status, body := client.PostRulesGroupWithStatus(t, folderUID, &group, false)
require.Equalf(t, http.StatusAccepted, status, "failed to post rule group. Response: %s", body)
// and the value of the missing_series_evals_to_resolve
getGroup, status := client.GetRulesGroup(t, folderUID, group.Name)
require.Equal(t, http.StatusAccepted, status)
if tc.initialValue == nil {
require.Nil(t, getGroup.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve)
} else {
require.Equal(t, *tc.initialValue, *getGroup.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve)
}
// Now let's update the initial value with the updated value
group = convertGettableRuleGroupToPostable(getGroup.GettableRuleGroupConfig)
group.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve = tc.updatedValue
_, status, body = client.PostRulesGroupWithStatus(t, folderUID, &group, false)
require.Equalf(t, tc.expectedStatus, status, "failed to post rule group. Response: %s", body)
// Check the response status
require.Equal(t, tc.expectedStatus, status)
if tc.expectedStatus != http.StatusAccepted {
// If the status is not accepted, we don't need to check the response body
return
}
// Get the group again and check that the value is updated to updatedValue
getGroup, status = client.GetRulesGroup(t, folderUID, group.Name)
require.Equal(t, http.StatusAccepted, status)
if tc.expectedValue == nil {
require.Nil(t, getGroup.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve)
} else {
require.Equal(t, *tc.expectedValue, *getGroup.Rules[0].GrafanaManagedAlert.MissingSeriesEvalsToResolve)
}
})
}
})
t.Run("when data source missing", func(t *testing.T) {
var groupName string
{
@@ -3362,16 +3450,17 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
}`),
},
},
NoDataState: apimodels.NoDataState(ngmodels.Alerting),
ExecErrState: apimodels.ExecutionErrorState(ngmodels.AlertingErrState),
NoDataState: apimodels.NoDataState(ngmodels.Alerting),
ExecErrState: apimodels.ExecutionErrorState(ngmodels.AlertingErrState),
MissingSeriesEvalsToResolve: util.Pointer(2), // If UID is specified, this field is required
},
},
},
Interval: interval,
}
response, status, _ := apiClient.PostRulesGroupWithStatus(t, "default", &rules, false)
assert.Equal(t, http.StatusAccepted, status)
response, status, body := apiClient.PostRulesGroupWithStatus(t, "default", &rules, false)
assert.Equal(t, http.StatusAccepted, status, body)
require.Len(t, response.Created, 1)
require.Len(t, response.Updated, 2)