diff --git a/pkg/services/ngalert/api/api_provisioning.go b/pkg/services/ngalert/api/api_provisioning.go index 8c7e68eb704..7514d0be256 100644 --- a/pkg/services/ngalert/api/api_provisioning.go +++ b/pkg/services/ngalert/api/api_provisioning.go @@ -15,6 +15,8 @@ import ( "github.com/grafana/grafana/pkg/util" ) +const disableProvenanceHeaderName = "X-Disable-Provenance" + type ProvisioningSrv struct { log log.Logger policies NotificationPolicyService @@ -259,7 +261,8 @@ func (srv *ProvisioningSrv) RoutePostAlertRule(c *models.ReqContext, ar definiti if err != nil { return ErrResp(http.StatusBadRequest, err, "") } - createdAlertRule, err := srv.alertRules.CreateAlertRule(c.Req.Context(), upstreamModel, alerting_models.ProvenanceAPI, c.UserID) + provenance := determineProvenance(c) + createdAlertRule, err := srv.alertRules.CreateAlertRule(c.Req.Context(), upstreamModel, provenance, c.UserID) if errors.Is(err, alerting_models.ErrAlertRuleFailedValidation) { return ErrResp(http.StatusBadRequest, err, "") } @@ -273,7 +276,7 @@ func (srv *ProvisioningSrv) RoutePostAlertRule(c *models.ReqContext, ar definiti return ErrResp(http.StatusInternalServerError, err, "") } - resp := definitions.NewAlertRule(createdAlertRule, alerting_models.ProvenanceAPI) + resp := definitions.NewAlertRule(createdAlertRule, provenance) return response.JSON(http.StatusCreated, resp) } @@ -284,7 +287,8 @@ func (srv *ProvisioningSrv) RoutePutAlertRule(c *models.ReqContext, ar definitio } updated.OrgID = c.OrgID updated.UID = UID - updatedAlertRule, err := srv.alertRules.UpdateAlertRule(c.Req.Context(), updated, alerting_models.ProvenanceAPI) + provenance := determineProvenance(c) + updatedAlertRule, err := srv.alertRules.UpdateAlertRule(c.Req.Context(), updated, provenance) if errors.Is(err, alerting_models.ErrAlertRuleNotFound) { return response.Empty(http.StatusNotFound) } @@ -298,7 +302,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRule(c *models.ReqContext, ar definitio return ErrResp(http.StatusInternalServerError, err, "") } - resp := definitions.NewAlertRule(updatedAlertRule, alerting_models.ProvenanceAPI) + resp := definitions.NewAlertRule(updatedAlertRule, provenance) return response.JSON(http.StatusOK, resp) } @@ -340,3 +344,10 @@ func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *models.ReqContext, ag defi } return response.JSON(http.StatusOK, ag) } + +func determineProvenance(ctx *models.ReqContext) alerting_models.Provenance { + if _, disabled := ctx.Req.Header[disableProvenanceHeaderName]; disabled { + return alerting_models.ProvenanceNone + } + return alerting_models.ProvenanceAPI +} diff --git a/pkg/services/ngalert/api/api_provisioning_test.go b/pkg/services/ngalert/api/api_provisioning_test.go index c5ca03acda9..4dd16558ddf 100644 --- a/pkg/services/ngalert/api/api_provisioning_test.go +++ b/pkg/services/ngalert/api/api_provisioning_test.go @@ -229,7 +229,7 @@ func TestProvisioningApi(t *testing.T) { t.Run("alert rules", func(t *testing.T) { t.Run("are invalid", func(t *testing.T) { - t.Run("POST returns 400", func(t *testing.T) { + t.Run("POST returns 400 on wrong body params", func(t *testing.T) { sut := createProvisioningSrvSut(t) rc := createTestRequestCtx() rule := createInvalidAlertRule() @@ -241,7 +241,7 @@ func TestProvisioningApi(t *testing.T) { require.Contains(t, string(response.Body()), "invalid alert rule") }) - t.Run("PUT returns 400", func(t *testing.T) { + t.Run("PUT returns 400 on wrong body params", func(t *testing.T) { sut := createProvisioningSrvSut(t) rc := createTestRequestCtx() uid := "123123" @@ -258,9 +258,10 @@ func TestProvisioningApi(t *testing.T) { }) t.Run("exist in non-default orgs", func(t *testing.T) { - t.Run("POST sets expected fields", func(t *testing.T) { + t.Run("POST sets expected fields with no provenance", func(t *testing.T) { sut := createProvisioningSrvSut(t) rc := createTestRequestCtx() + rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"true"}} rc.OrgID = 3 rule := createTestAlertRule("rule", 1) @@ -269,15 +270,17 @@ func TestProvisioningApi(t *testing.T) { require.Equal(t, 201, response.Status()) created := deserializeRule(t, response.Body()) require.Equal(t, int64(3), created.OrgID) + require.Equal(t, models.ProvenanceNone, created.Provenance) }) - t.Run("PUT sets expected fields", func(t *testing.T) { + t.Run("PUT sets expected fields with no provenance", func(t *testing.T) { sut := createProvisioningSrvSut(t) uid := t.Name() rule := createTestAlertRule("rule", 1) rule.UID = uid insertRuleInOrg(t, sut, rule, 3) rc := createTestRequestCtx() + rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"hello"}} rc.OrgID = 3 rule.OrgID = 1 // Set the org back to something wrong, we should still prefer the value from the req context. @@ -286,6 +289,7 @@ func TestProvisioningApi(t *testing.T) { require.Equal(t, 200, response.Status()) created := deserializeRule(t, response.Body()) require.Equal(t, int64(3), created.OrgID) + require.Equal(t, models.ProvenanceNone, created.Provenance) }) }) diff --git a/pkg/services/ngalert/api/tooling/api.json b/pkg/services/ngalert/api/tooling/api.json index 69cadd763a2..d46b63b94a1 100644 --- a/pkg/services/ngalert/api/tooling/api.json +++ b/pkg/services/ngalert/api/tooling/api.json @@ -364,11 +364,14 @@ "description": "A map of RefIDs (unique query identifiers) to this type makes up the Responses property of a QueryDataResponse.\nThe Error property is used to allow for partial success responses from the containing QueryDataResponse.", "properties": { "Error": { - "description": "Error is a property to be set if the the corresponding DataQuery has an error.", + "description": "Error is a property to be set if the corresponding DataQuery has an error.", "type": "string" }, "Frames": { "$ref": "#/definitions/Frames" + }, + "Status": { + "$ref": "#/definitions/Status" } }, "title": "DataResponse contains the results from a DataQuery.", @@ -2816,6 +2819,10 @@ "SmtpNotEnabled": { "$ref": "#/definitions/ResponseDetails" }, + "Status": { + "format": "int64", + "type": "integer" + }, "Success": { "$ref": "#/definitions/ResponseDetails" }, @@ -3070,6 +3077,7 @@ "type": "object" }, "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", "properties": { "ForceQuery": { "type": "boolean" @@ -3102,7 +3110,7 @@ "$ref": "#/definitions/Userinfo" } }, - "title": "URL is a custom URL type that allows validation at configuration load time.", + "title": "A URL represents a parsed URL (technically, a URI reference).", "type": "object" }, "Userinfo": { @@ -3258,6 +3266,7 @@ "type": "object" }, "alertGroup": { + "description": "AlertGroup alert group", "properties": { "alerts": { "description": "alerts", @@ -3281,6 +3290,7 @@ "type": "object" }, "alertGroups": { + "description": "AlertGroups alert groups", "items": { "$ref": "#/definitions/alertGroup" }, @@ -3385,7 +3395,6 @@ "type": "object" }, "gettableAlert": { - "description": "GettableAlert gettable alert", "properties": { "annotations": { "$ref": "#/definitions/labelSet" @@ -3441,12 +3450,14 @@ "type": "object" }, "gettableAlerts": { + "description": "GettableAlerts gettable alerts", "items": { "$ref": "#/definitions/gettableAlert" }, "type": "array" }, "gettableSilence": { + "description": "GettableSilence gettable silence", "properties": { "comment": { "description": "comment", @@ -3495,6 +3506,7 @@ "type": "object" }, "gettableSilences": { + "description": "GettableSilences gettable silences", "items": { "$ref": "#/definitions/gettableSilence" }, @@ -3683,7 +3695,6 @@ "type": "object" }, "receiver": { - "description": "Receiver receiver", "properties": { "active": { "description": "active", @@ -3816,6 +3827,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "in": "header", + "name": "X-Disable-Provenance", + "type": "string" } ], "responses": { @@ -3906,6 +3922,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "in": "header", + "name": "X-Disable-Provenance", + "type": "string" } ], "responses": { diff --git a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go index c24a53a5910..75c86d20b39 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go @@ -57,6 +57,12 @@ type AlertRulePayload struct { Body ProvisionedAlertRule } +// swagger:parameters RoutePostAlertRule RoutePutAlertRule +type AlertRuleHeaders struct { + // in:header + XDisableProvenance string `json:"X-Disable-Provenance"` +} + type ProvisionedAlertRule struct { ID int64 `json:"id"` UID string `json:"uid"` diff --git a/pkg/services/ngalert/api/tooling/post.json b/pkg/services/ngalert/api/tooling/post.json index 1c32bbdf7be..09e0e92780f 100644 --- a/pkg/services/ngalert/api/tooling/post.json +++ b/pkg/services/ngalert/api/tooling/post.json @@ -364,11 +364,14 @@ "description": "A map of RefIDs (unique query identifiers) to this type makes up the Responses property of a QueryDataResponse.\nThe Error property is used to allow for partial success responses from the containing QueryDataResponse.", "properties": { "Error": { - "description": "Error is a property to be set if the the corresponding DataQuery has an error.", + "description": "Error is a property to be set if the corresponding DataQuery has an error.", "type": "string" }, "Frames": { "$ref": "#/definitions/Frames" + }, + "Status": { + "$ref": "#/definitions/Status" } }, "title": "DataResponse contains the results from a DataQuery.", @@ -2816,6 +2819,10 @@ "SmtpNotEnabled": { "$ref": "#/definitions/ResponseDetails" }, + "Status": { + "format": "int64", + "type": "integer" + }, "Success": { "$ref": "#/definitions/ResponseDetails" }, @@ -3283,7 +3290,6 @@ "type": "object" }, "alertGroups": { - "description": "AlertGroups alert groups", "items": { "$ref": "#/definitions/alertGroup" }, @@ -3443,6 +3449,7 @@ "type": "object" }, "gettableAlerts": { + "description": "GettableAlerts gettable alerts", "items": { "$ref": "#/definitions/gettableAlert" }, @@ -3685,6 +3692,7 @@ "type": "object" }, "receiver": { + "description": "Receiver receiver", "properties": { "active": { "description": "active", @@ -5489,6 +5497,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "in": "header", + "name": "X-Disable-Provenance", + "type": "string" } ], "responses": { @@ -5579,6 +5592,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "in": "header", + "name": "X-Disable-Provenance", + "type": "string" } ], "responses": { diff --git a/pkg/services/ngalert/api/tooling/spec.json b/pkg/services/ngalert/api/tooling/spec.json index 91428fd6cd3..feabdcf0e78 100644 --- a/pkg/services/ngalert/api/tooling/spec.json +++ b/pkg/services/ngalert/api/tooling/spec.json @@ -1707,6 +1707,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "type": "string", + "name": "X-Disable-Provenance", + "in": "header" } ], "responses": { @@ -1778,6 +1783,11 @@ "schema": { "$ref": "#/definitions/ProvisionedAlertRule" } + }, + { + "type": "string", + "name": "X-Disable-Provenance", + "in": "header" } ], "responses": { @@ -2799,11 +2809,14 @@ "title": "DataResponse contains the results from a DataQuery.", "properties": { "Error": { - "description": "Error is a property to be set if the the corresponding DataQuery has an error.", + "description": "Error is a property to be set if the corresponding DataQuery has an error.", "type": "string" }, "Frames": { "$ref": "#/definitions/Frames" + }, + "Status": { + "$ref": "#/definitions/Status" } } }, @@ -5253,6 +5266,10 @@ "SmtpNotEnabled": { "$ref": "#/definitions/ResponseDetails" }, + "Status": { + "type": "integer", + "format": "int64" + }, "Success": { "$ref": "#/definitions/ResponseDetails" }, @@ -5721,7 +5738,6 @@ "$ref": "#/definitions/alertGroup" }, "alertGroups": { - "description": "AlertGroups alert groups", "type": "array", "items": { "$ref": "#/definitions/alertGroup" @@ -5883,6 +5899,7 @@ "$ref": "#/definitions/gettableAlert" }, "gettableAlerts": { + "description": "GettableAlerts gettable alerts", "type": "array", "items": { "$ref": "#/definitions/gettableAlert" @@ -6130,6 +6147,7 @@ "$ref": "#/definitions/postableSilence" }, "receiver": { + "description": "Receiver receiver", "type": "object", "required": [ "active",