Alerting: Add provisioning endpoint to fetch all rules (#59989)

* Domain layer api for fetching all rules

* Add endpoint for getting all rules
pull/60227/head
Alexander Weaver 3 years ago committed by GitHub
parent d6bd3c4fb6
commit e97b43cd58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      pkg/services/ngalert/api/api_provisioning.go
  2. 1
      pkg/services/ngalert/api/authorization.go
  3. 14
      pkg/services/ngalert/api/generated_base_api_provisioning.go
  4. 4
      pkg/services/ngalert/api/provisioning.go
  5. 30
      pkg/services/ngalert/api/tooling/api.json
  6. 18
      pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go
  7. 31
      pkg/services/ngalert/api/tooling/post.json
  8. 32
      pkg/services/ngalert/api/tooling/spec.json
  9. 12
      pkg/services/ngalert/provisioning/alert_rules.go
  10. 30
      public/api-merged.json

@ -53,6 +53,7 @@ type MuteTimingService interface {
} }
type AlertRuleService interface { type AlertRuleService interface {
GetAlertRules(ctx context.Context, orgID int64) ([]*alerting_models.AlertRule, error)
GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (alerting_models.AlertRule, alerting_models.Provenance, error) GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (alerting_models.AlertRule, alerting_models.Provenance, error)
CreateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance, userID int64) (alerting_models.AlertRule, error) CreateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance, userID int64) (alerting_models.AlertRule, error)
UpdateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) UpdateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error)
@ -247,6 +248,14 @@ func (srv *ProvisioningSrv) RouteDeleteMuteTiming(c *models.ReqContext, name str
return response.JSON(http.StatusNoContent, nil) return response.JSON(http.StatusNoContent, nil)
} }
func (srv *ProvisioningSrv) RouteGetAlertRules(c *models.ReqContext) response.Response {
rules, err := srv.alertRules.GetAlertRules(c.Req.Context(), c.OrgID)
if err != nil {
return ErrResp(http.StatusInternalServerError, err, "")
}
return response.JSON(http.StatusOK, definitions.NewAlertRules(rules))
}
func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *models.ReqContext, UID string) response.Response { func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *models.ReqContext, UID string) response.Response {
rule, provenace, err := srv.alertRules.GetAlertRule(c.Req.Context(), c.OrgID, UID) rule, provenace, err := srv.alertRules.GetAlertRule(c.Req.Context(), c.OrgID, UID)
if err != nil { if err != nil {

@ -194,6 +194,7 @@ func (api *API) authorize(method, path string) web.Handler {
http.MethodGet + "/api/v1/provisioning/templates/{name}", http.MethodGet + "/api/v1/provisioning/templates/{name}",
http.MethodGet + "/api/v1/provisioning/mute-timings", http.MethodGet + "/api/v1/provisioning/mute-timings",
http.MethodGet + "/api/v1/provisioning/mute-timings/{name}", http.MethodGet + "/api/v1/provisioning/mute-timings/{name}",
http.MethodGet + "/api/v1/provisioning/alert-rules",
http.MethodGet + "/api/v1/provisioning/alert-rules/{UID}", http.MethodGet + "/api/v1/provisioning/alert-rules/{UID}",
http.MethodGet + "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": http.MethodGet + "/api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}":
fallback = middleware.ReqOrgAdmin fallback = middleware.ReqOrgAdmin

@ -25,6 +25,7 @@ type ProvisioningApi interface {
RouteDeleteTemplate(*models.ReqContext) response.Response RouteDeleteTemplate(*models.ReqContext) response.Response
RouteGetAlertRule(*models.ReqContext) response.Response RouteGetAlertRule(*models.ReqContext) response.Response
RouteGetAlertRuleGroup(*models.ReqContext) response.Response RouteGetAlertRuleGroup(*models.ReqContext) response.Response
RouteGetAlertRules(*models.ReqContext) response.Response
RouteGetContactpoints(*models.ReqContext) response.Response RouteGetContactpoints(*models.ReqContext) response.Response
RouteGetMuteTiming(*models.ReqContext) response.Response RouteGetMuteTiming(*models.ReqContext) response.Response
RouteGetMuteTimings(*models.ReqContext) response.Response RouteGetMuteTimings(*models.ReqContext) response.Response
@ -74,6 +75,9 @@ func (f *ProvisioningApiHandler) RouteGetAlertRuleGroup(ctx *models.ReqContext)
groupParam := web.Params(ctx.Req)[":Group"] groupParam := web.Params(ctx.Req)[":Group"]
return f.handleRouteGetAlertRuleGroup(ctx, folderUIDParam, groupParam) return f.handleRouteGetAlertRuleGroup(ctx, folderUIDParam, groupParam)
} }
func (f *ProvisioningApiHandler) RouteGetAlertRules(ctx *models.ReqContext) response.Response {
return f.handleRouteGetAlertRules(ctx)
}
func (f *ProvisioningApiHandler) RouteGetContactpoints(ctx *models.ReqContext) response.Response { func (f *ProvisioningApiHandler) RouteGetContactpoints(ctx *models.ReqContext) response.Response {
return f.handleRouteGetContactpoints(ctx) return f.handleRouteGetContactpoints(ctx)
} }
@ -245,6 +249,16 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics
m, m,
), ),
) )
group.Get(
toMacaronPath("/api/v1/provisioning/alert-rules"),
api.authorize(http.MethodGet, "/api/v1/provisioning/alert-rules"),
metrics.Instrument(
http.MethodGet,
"/api/v1/provisioning/alert-rules",
srv.RouteGetAlertRules,
m,
),
)
group.Get( group.Get(
toMacaronPath("/api/v1/provisioning/contact-points"), toMacaronPath("/api/v1/provisioning/contact-points"),
api.authorize(http.MethodGet, "/api/v1/provisioning/contact-points"), api.authorize(http.MethodGet, "/api/v1/provisioning/contact-points"),

@ -76,6 +76,10 @@ func (f *ProvisioningApiHandler) handleRouteDeleteMuteTiming(ctx *models.ReqCont
return f.svc.RouteDeleteMuteTiming(ctx, name) return f.svc.RouteDeleteMuteTiming(ctx, name)
} }
func (f *ProvisioningApiHandler) handleRouteGetAlertRules(ctx *models.ReqContext) response.Response {
return f.svc.RouteGetAlertRules(ctx)
}
func (f *ProvisioningApiHandler) handleRouteGetAlertRule(ctx *models.ReqContext, UID string) response.Response { func (f *ProvisioningApiHandler) handleRouteGetAlertRule(ctx *models.ReqContext, UID string) response.Response {
return f.svc.RouteRouteGetAlertRule(ctx, UID) return f.svc.RouteRouteGetAlertRule(ctx, UID)
} }

@ -2164,6 +2164,12 @@
], ],
"type": "object" "type": "object"
}, },
"ProvisionedAlertRules": {
"items": {
"$ref": "#/definitions/ProvisionedAlertRule"
},
"type": "array"
},
"PushoverConfig": { "PushoverConfig": {
"properties": { "properties": {
"expire": { "expire": {
@ -3077,7 +3083,6 @@
"type": "object" "type": "object"
}, },
"URL": { "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": { "properties": {
"ForceQuery": { "ForceQuery": {
"type": "boolean" "type": "boolean"
@ -3088,6 +3093,9 @@
"Host": { "Host": {
"type": "string" "type": "string"
}, },
"OmitHost": {
"type": "boolean"
},
"Opaque": { "Opaque": {
"type": "string" "type": "string"
}, },
@ -3110,7 +3118,7 @@
"$ref": "#/definitions/Userinfo" "$ref": "#/definitions/Userinfo"
} }
}, },
"title": "A URL represents a parsed URL (technically, a URI reference).", "title": "URL is a custom URL type that allows validation at configuration load time.",
"type": "object" "type": "object"
}, },
"Userinfo": { "Userinfo": {
@ -3395,6 +3403,7 @@
"type": "object" "type": "object"
}, },
"gettableAlert": { "gettableAlert": {
"description": "GettableAlert gettable alert",
"properties": { "properties": {
"annotations": { "annotations": {
"$ref": "#/definitions/labelSet" "$ref": "#/definitions/labelSet"
@ -3513,7 +3522,6 @@
"type": "array" "type": "array"
}, },
"integration": { "integration": {
"description": "Integration integration",
"properties": { "properties": {
"lastNotifyAttempt": { "lastNotifyAttempt": {
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time", "description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
@ -3657,7 +3665,6 @@
"type": "array" "type": "array"
}, },
"postableSilence": { "postableSilence": {
"description": "PostableSilence postable silence",
"properties": { "properties": {
"comment": { "comment": {
"description": "comment", "description": "comment",
@ -3815,6 +3822,21 @@
}, },
"paths": { "paths": {
"/api/v1/provisioning/alert-rules": { "/api/v1/provisioning/alert-rules": {
"get": {
"operationId": "RouteGetAlertRules",
"responses": {
"200": {
"description": "ProvisionedAlertRules",
"schema": {
"$ref": "#/definitions/ProvisionedAlertRules"
}
}
},
"summary": "Get all the alert rules.",
"tags": [
"provisioning"
]
},
"post": { "post": {
"consumes": [ "consumes": [
"application/json" "application/json"

@ -7,6 +7,13 @@ import (
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
) )
// swagger:route GET /api/v1/provisioning/alert-rules provisioning stable RouteGetAlertRules
//
// Get all the alert rules.
//
// Responses:
// 200: ProvisionedAlertRules
// swagger:route GET /api/v1/provisioning/alert-rules/{UID} provisioning stable RouteGetAlertRule // swagger:route GET /api/v1/provisioning/alert-rules/{UID} provisioning stable RouteGetAlertRule
// //
// Get a specific alert rule by UID. // Get a specific alert rule by UID.
@ -63,6 +70,9 @@ type AlertRuleHeaders struct {
XDisableProvenance string `json:"X-Disable-Provenance"` XDisableProvenance string `json:"X-Disable-Provenance"`
} }
// swagger:model
type ProvisionedAlertRules []ProvisionedAlertRule
type ProvisionedAlertRule struct { type ProvisionedAlertRule struct {
ID int64 `json:"id"` ID int64 `json:"id"`
UID string `json:"uid"` UID string `json:"uid"`
@ -142,6 +152,14 @@ func NewAlertRule(rule models.AlertRule, provenance models.Provenance) Provision
} }
} }
func NewAlertRules(rules []*models.AlertRule) ProvisionedAlertRules {
result := make([]ProvisionedAlertRule, 0, len(rules))
for _, r := range rules {
result = append(result, NewAlertRule(*r, models.ProvenanceNone))
}
return result
}
// swagger:route GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RouteGetAlertRuleGroup // swagger:route GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RouteGetAlertRuleGroup
// //
// Get a rule group. // Get a rule group.

@ -2164,6 +2164,12 @@
], ],
"type": "object" "type": "object"
}, },
"ProvisionedAlertRules": {
"items": {
"$ref": "#/definitions/ProvisionedAlertRule"
},
"type": "array"
},
"PushoverConfig": { "PushoverConfig": {
"properties": { "properties": {
"expire": { "expire": {
@ -3077,7 +3083,6 @@
"type": "object" "type": "object"
}, },
"URL": { "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": { "properties": {
"ForceQuery": { "ForceQuery": {
"type": "boolean" "type": "boolean"
@ -3088,6 +3093,9 @@
"Host": { "Host": {
"type": "string" "type": "string"
}, },
"OmitHost": {
"type": "boolean"
},
"Opaque": { "Opaque": {
"type": "string" "type": "string"
}, },
@ -3110,7 +3118,7 @@
"$ref": "#/definitions/Userinfo" "$ref": "#/definitions/Userinfo"
} }
}, },
"title": "A URL represents a parsed URL (technically, a URI reference).", "title": "URL is a custom URL type that allows validation at configuration load time.",
"type": "object" "type": "object"
}, },
"Userinfo": { "Userinfo": {
@ -3266,7 +3274,6 @@
"type": "object" "type": "object"
}, },
"alertGroup": { "alertGroup": {
"description": "AlertGroup alert group",
"properties": { "properties": {
"alerts": { "alerts": {
"description": "alerts", "description": "alerts",
@ -3456,7 +3463,6 @@
"type": "array" "type": "array"
}, },
"gettableSilence": { "gettableSilence": {
"description": "GettableSilence gettable silence",
"properties": { "properties": {
"comment": { "comment": {
"description": "comment", "description": "comment",
@ -3511,6 +3517,7 @@
"type": "array" "type": "array"
}, },
"integration": { "integration": {
"description": "Integration integration",
"properties": { "properties": {
"lastNotifyAttempt": { "lastNotifyAttempt": {
"description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time", "description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time",
@ -3654,7 +3661,6 @@
"type": "array" "type": "array"
}, },
"postableSilence": { "postableSilence": {
"description": "PostableSilence postable silence",
"properties": { "properties": {
"comment": { "comment": {
"description": "comment", "description": "comment",
@ -5485,6 +5491,21 @@
} }
}, },
"/api/v1/provisioning/alert-rules": { "/api/v1/provisioning/alert-rules": {
"get": {
"operationId": "RouteGetAlertRules",
"responses": {
"200": {
"description": "ProvisionedAlertRules",
"schema": {
"$ref": "#/definitions/ProvisionedAlertRules"
}
}
},
"summary": "Get all the alert rules.",
"tags": [
"provisioning"
]
},
"post": { "post": {
"consumes": [ "consumes": [
"application/json" "application/json"

@ -1690,6 +1690,22 @@
} }
}, },
"/api/v1/provisioning/alert-rules": { "/api/v1/provisioning/alert-rules": {
"get": {
"tags": [
"provisioning",
"stable"
],
"summary": "Get all the alert rules.",
"operationId": "RouteGetAlertRules",
"responses": {
"200": {
"description": "ProvisionedAlertRules",
"schema": {
"$ref": "#/definitions/ProvisionedAlertRules"
}
}
}
},
"post": { "post": {
"consumes": [ "consumes": [
"application/json" "application/json"
@ -4611,6 +4627,12 @@
} }
} }
}, },
"ProvisionedAlertRules": {
"type": "array",
"items": {
"$ref": "#/definitions/ProvisionedAlertRule"
}
},
"PushoverConfig": { "PushoverConfig": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -5524,9 +5546,8 @@
} }
}, },
"URL": { "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.",
"type": "object", "type": "object",
"title": "A URL represents a parsed URL (technically, a URI reference).", "title": "URL is a custom URL type that allows validation at configuration load time.",
"properties": { "properties": {
"ForceQuery": { "ForceQuery": {
"type": "boolean" "type": "boolean"
@ -5537,6 +5558,9 @@
"Host": { "Host": {
"type": "string" "type": "string"
}, },
"OmitHost": {
"type": "boolean"
},
"Opaque": { "Opaque": {
"type": "string" "type": "string"
}, },
@ -5713,7 +5737,6 @@
} }
}, },
"alertGroup": { "alertGroup": {
"description": "AlertGroup alert group",
"type": "object", "type": "object",
"required": [ "required": [
"alerts", "alerts",
@ -5907,7 +5930,6 @@
"$ref": "#/definitions/gettableAlerts" "$ref": "#/definitions/gettableAlerts"
}, },
"gettableSilence": { "gettableSilence": {
"description": "GettableSilence gettable silence",
"type": "object", "type": "object",
"required": [ "required": [
"comment", "comment",
@ -5964,6 +5986,7 @@
"$ref": "#/definitions/gettableSilences" "$ref": "#/definitions/gettableSilences"
}, },
"integration": { "integration": {
"description": "Integration integration",
"type": "object", "type": "object",
"required": [ "required": [
"name", "name",
@ -6108,7 +6131,6 @@
} }
}, },
"postableSilence": { "postableSilence": {
"description": "PostableSilence postable silence",
"type": "object", "type": "object",
"required": [ "required": [
"comment", "comment",

@ -41,6 +41,18 @@ func NewAlertRuleService(ruleStore RuleStore,
} }
} }
func (service *AlertRuleService) GetAlertRules(ctx context.Context, orgID int64) ([]*models.AlertRule, error) {
q := models.ListAlertRulesQuery{
OrgID: orgID,
}
err := service.ruleStore.ListAlertRules(ctx, &q)
if err != nil {
return nil, err
}
// TODO: GET provenance
return q.Result, nil
}
func (service *AlertRuleService) GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (models.AlertRule, models.Provenance, error) { func (service *AlertRuleService) GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (models.AlertRule, models.Provenance, error) {
query := &models.GetAlertRuleByUIDQuery{ query := &models.GetAlertRuleByUIDQuery{
OrgID: orgID, OrgID: orgID,

@ -2442,6 +2442,21 @@
} }
}, },
"/api/v1/provisioning/alert-rules": { "/api/v1/provisioning/alert-rules": {
"get": {
"tags": [
"provisioning"
],
"summary": "Get all the alert rules.",
"operationId": "RouteGetAlertRules",
"responses": {
"200": {
"description": "ProvisionedAlertRules",
"schema": {
"$ref": "#/definitions/ProvisionedAlertRules"
}
}
}
},
"post": { "post": {
"consumes": [ "consumes": [
"application/json" "application/json"
@ -15587,6 +15602,12 @@
} }
} }
}, },
"ProvisionedAlertRules": {
"type": "array",
"items": {
"$ref": "#/definitions/ProvisionedAlertRule"
}
},
"PushoverConfig": { "PushoverConfig": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -17547,9 +17568,8 @@
"type": "string" "type": "string"
}, },
"URL": { "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.",
"type": "object", "type": "object",
"title": "A URL represents a parsed URL (technically, a URI reference).", "title": "URL is a custom URL type that allows validation at configuration load time.",
"properties": { "properties": {
"ForceQuery": { "ForceQuery": {
"type": "boolean" "type": "boolean"
@ -17560,6 +17580,9 @@
"Host": { "Host": {
"type": "string" "type": "string"
}, },
"OmitHost": {
"type": "boolean"
},
"Opaque": { "Opaque": {
"type": "string" "type": "string"
}, },
@ -18484,6 +18507,7 @@
} }
}, },
"gettableAlert": { "gettableAlert": {
"description": "GettableAlert gettable alert",
"type": "object", "type": "object",
"required": [ "required": [
"labels", "labels",
@ -18602,7 +18626,6 @@
} }
}, },
"integration": { "integration": {
"description": "Integration integration",
"type": "object", "type": "object",
"required": [ "required": [
"name", "name",
@ -18746,7 +18769,6 @@
} }
}, },
"postableSilence": { "postableSilence": {
"description": "PostableSilence postable silence",
"type": "object", "type": "object",
"required": [ "required": [
"comment", "comment",

Loading…
Cancel
Save