Alerting: Allow none provenance alert rule creation from provisioning API (#58410)

This commit is contained in:
Alex Moreno 2022-11-11 19:58:45 +01:00 committed by GitHub
parent 79142340e0
commit 78bb8c10ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 16 deletions

View File

@ -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
}

View File

@ -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)
})
})

View File

@ -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": {

View File

@ -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"`

View File

@ -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": {

View File

@ -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",