Alerting: Fix incorrect 500 code on missing alert rule dashboardUID / panelID (#96491)

This commit is contained in:
Matthew Jacobson 2024-11-14 14:24:48 -05:00 committed by GitHub
parent 97347a1f94
commit 64c93217ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 90 additions and 21 deletions

View File

@ -301,6 +301,36 @@ func TestValidateRuleGroupFailures(t *testing.T) {
require.Contains(t, err.Error(), apiModel.Rules[0].GrafanaManagedAlert.UID) require.Contains(t, err.Error(), apiModel.Rules[0].GrafanaManagedAlert.UID)
}, },
}, },
{
name: "fail with 4xx if rule contains only panelID",
group: func() *apimodels.PostableRuleGroupConfig {
r1 := validRule()
panelId := int64(42)
r1.Annotations = map[string]string{
models.PanelIDAnnotation: strconv.FormatInt(panelId, 10),
}
g := validGroup(cfg, r1)
return &g
},
assert: func(t *testing.T, apiModel *apimodels.PostableRuleGroupConfig, err error) {
require.ErrorIs(t, err, models.ErrAlertRuleFailedValidation)
},
},
{
name: "fail with 4xx if rule contains only dashboardUID",
group: func() *apimodels.PostableRuleGroupConfig {
r1 := validRule()
dashboardUid := "oinwerfgiuac"
r1.Annotations = map[string]string{
models.DashboardUIDAnnotation: dashboardUid,
}
g := validGroup(cfg, r1)
return &g
},
assert: func(t *testing.T, apiModel *apimodels.PostableRuleGroupConfig, err error) {
require.ErrorIs(t, err, models.ErrAlertRuleFailedValidation)
},
},
} }
for _, testCase := range testCases { for _, testCase := range testCases {

View File

@ -436,13 +436,13 @@ func (alertRule *AlertRule) SetDashboardAndPanelFromAnnotations() error {
dashUID := alertRule.Annotations[DashboardUIDAnnotation] dashUID := alertRule.Annotations[DashboardUIDAnnotation]
panelID := alertRule.Annotations[PanelIDAnnotation] panelID := alertRule.Annotations[PanelIDAnnotation]
if dashUID != "" && panelID == "" || dashUID == "" && panelID != "" { if dashUID != "" && panelID == "" || dashUID == "" && panelID != "" {
return fmt.Errorf("both annotations %s and %s must be specified", return fmt.Errorf("%w: both annotations %s and %s must be specified", ErrAlertRuleFailedValidation,
DashboardUIDAnnotation, PanelIDAnnotation) DashboardUIDAnnotation, PanelIDAnnotation)
} }
if dashUID != "" { if dashUID != "" {
panelIDValue, err := strconv.ParseInt(panelID, 10, 64) panelIDValue, err := strconv.ParseInt(panelID, 10, 64)
if err != nil { if err != nil {
return fmt.Errorf("annotation %s must be a valid integer Panel ID", return fmt.Errorf("%w: annotation %s must be a valid integer Panel ID", ErrAlertRuleFailedValidation,
PanelIDAnnotation) PanelIDAnnotation)
} }
alertRule.DashboardUID = &dashUID alertRule.DashboardUID = &dashUID

View File

@ -137,6 +137,7 @@ func TestSetDashboardAndPanelFromAnnotations(t *testing.T) {
name string name string
annotations map[string]string annotations map[string]string
expectedError error expectedError error
expectedErrContains string
expectedDashboardUID string expectedDashboardUID string
expectedPanelID int64 expectedPanelID int64
}{ }{
@ -148,41 +149,42 @@ func TestSetDashboardAndPanelFromAnnotations(t *testing.T) {
expectedPanelID: -1, expectedPanelID: -1,
}, },
{ {
name: "dashboardUID is not present", name: "dashboardUID is not present",
annotations: map[string]string{PanelIDAnnotation: "1234567890"}, annotations: map[string]string{PanelIDAnnotation: "1234567890"},
expectedError: fmt.Errorf("both annotations %s and %s must be specified", expectedError: ErrAlertRuleFailedValidation,
DashboardUIDAnnotation, PanelIDAnnotation), expectedErrContains: fmt.Sprintf("%s and %s", DashboardUIDAnnotation, PanelIDAnnotation),
expectedDashboardUID: "", expectedDashboardUID: "",
expectedPanelID: -1, expectedPanelID: -1,
}, },
{ {
name: "dashboardUID is present but empty", name: "dashboardUID is present but empty",
annotations: map[string]string{DashboardUIDAnnotation: "", PanelIDAnnotation: "1234567890"}, annotations: map[string]string{DashboardUIDAnnotation: "", PanelIDAnnotation: "1234567890"},
expectedError: fmt.Errorf("both annotations %s and %s must be specified", expectedError: ErrAlertRuleFailedValidation,
DashboardUIDAnnotation, PanelIDAnnotation), expectedErrContains: fmt.Sprintf("%s and %s", DashboardUIDAnnotation, PanelIDAnnotation),
expectedDashboardUID: "", expectedDashboardUID: "",
expectedPanelID: -1, expectedPanelID: -1,
}, },
{ {
name: "panelID is not present", name: "panelID is not present",
annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk"}, annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk"},
expectedError: fmt.Errorf("both annotations %s and %s must be specified", expectedError: ErrAlertRuleFailedValidation,
DashboardUIDAnnotation, PanelIDAnnotation), expectedErrContains: fmt.Sprintf("%s and %s", DashboardUIDAnnotation, PanelIDAnnotation),
expectedDashboardUID: "", expectedDashboardUID: "",
expectedPanelID: -1, expectedPanelID: -1,
}, },
{ {
name: "panelID is present but empty", name: "panelID is present but empty",
annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk", PanelIDAnnotation: ""}, annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk", PanelIDAnnotation: ""},
expectedError: fmt.Errorf("both annotations %s and %s must be specified", expectedError: ErrAlertRuleFailedValidation,
DashboardUIDAnnotation, PanelIDAnnotation), expectedErrContains: fmt.Sprintf("%s and %s", DashboardUIDAnnotation, PanelIDAnnotation),
expectedDashboardUID: "", expectedDashboardUID: "",
expectedPanelID: -1, expectedPanelID: -1,
}, },
{ {
name: "dashboardUID and panelID are present but panelID is not a correct int64", name: "dashboardUID and panelID are present but panelID is not a correct int64",
annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk", PanelIDAnnotation: "fgh"}, annotations: map[string]string{DashboardUIDAnnotation: "cKy7f6Hk", PanelIDAnnotation: "fgh"},
expectedError: fmt.Errorf("annotation %s must be a valid integer Panel ID", PanelIDAnnotation), expectedError: ErrAlertRuleFailedValidation,
expectedErrContains: PanelIDAnnotation,
expectedDashboardUID: "", expectedDashboardUID: "",
expectedPanelID: -1, expectedPanelID: -1,
}, },
@ -203,7 +205,10 @@ func TestSetDashboardAndPanelFromAnnotations(t *testing.T) {
).Generate() ).Generate()
err := rule.SetDashboardAndPanelFromAnnotations() err := rule.SetDashboardAndPanelFromAnnotations()
require.Equal(t, tc.expectedError, err) require.ErrorIs(t, err, tc.expectedError)
if tc.expectedErrContains != "" {
require.ErrorContains(t, err, tc.expectedErrContains)
}
require.Equal(t, tc.expectedDashboardUID, rule.GetDashboardUID()) require.Equal(t, tc.expectedDashboardUID, rule.GetDashboardUID())
require.Equal(t, tc.expectedPanelID, rule.GetPanelID()) require.Equal(t, tc.expectedPanelID, rule.GetPanelID())
}) })

View File

@ -867,6 +867,37 @@ func TestCreateAlertRule(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
}) })
}) })
t.Run("when dashboard is specified", func(t *testing.T) {
t.Run("return no error when both specified", func(t *testing.T) {
rule := dummyRule("test#4", orgID)
dashboardUid := "oinwerfgiuac"
panelId := int64(42)
rule.Annotations = map[string]string{
models.DashboardUIDAnnotation: dashboardUid,
models.PanelIDAnnotation: strconv.FormatInt(panelId, 10),
}
rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone)
require.NoError(t, err)
})
t.Run("return 4xx error when missing dashboard uid", func(t *testing.T) {
rule := dummyRule("test#3", orgID)
panelId := int64(42)
rule.Annotations = map[string]string{
models.PanelIDAnnotation: strconv.FormatInt(panelId, 10),
}
rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone)
require.ErrorIs(t, err, models.ErrAlertRuleFailedValidation)
})
t.Run("return 4xx error when missing panel id", func(t *testing.T) {
rule := dummyRule("test#3", orgID)
dashboardUid := "oinwerfgiuac"
rule.Annotations = map[string]string{
models.DashboardUIDAnnotation: dashboardUid,
}
rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone)
require.ErrorIs(t, err, models.ErrAlertRuleFailedValidation)
})
})
} }
func TestUpdateAlertRule(t *testing.T) { func TestUpdateAlertRule(t *testing.T) {

View File

@ -215,7 +215,10 @@ func TestIntegrationPrometheusRules(t *testing.T) {
assert.Equal(t, 400, resp.StatusCode) assert.Equal(t, 400, resp.StatusCode)
var res map[string]any var res map[string]any
require.NoError(t, json.Unmarshal(b, &res)) require.NoError(t, json.Unmarshal(b, &res))
require.Equal(t, "invalid rule specification at index [0]: both annotations __dashboardUid__ and __panelId__ must be specified", res["message"]) require.Contains(t, res["message"], "[0]") // Index of the invalid rule.
require.Contains(t, res["message"], ngmodels.ErrAlertRuleFailedValidation.Error())
require.Contains(t, res["message"], ngmodels.DashboardUIDAnnotation)
require.Contains(t, res["message"], ngmodels.PanelIDAnnotation)
} }
// Now, let's see how this looks like. // Now, let's see how this looks like.