diff --git a/pkg/services/ngalert/api/api_provisioning.go b/pkg/services/ngalert/api/api_provisioning.go index 7514d0be256..2c26f3e2a08 100644 --- a/pkg/services/ngalert/api/api_provisioning.go +++ b/pkg/services/ngalert/api/api_provisioning.go @@ -53,6 +53,7 @@ type MuteTimingService 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) 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) @@ -247,6 +248,14 @@ func (srv *ProvisioningSrv) RouteDeleteMuteTiming(c *models.ReqContext, name str 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 { rule, provenace, err := srv.alertRules.GetAlertRule(c.Req.Context(), c.OrgID, UID) if err != nil { diff --git a/pkg/services/ngalert/api/authorization.go b/pkg/services/ngalert/api/authorization.go index aa638568b7f..a8315c840d2 100644 --- a/pkg/services/ngalert/api/authorization.go +++ b/pkg/services/ngalert/api/authorization.go @@ -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/mute-timings", 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/folder/{FolderUID}/rule-groups/{Group}": fallback = middleware.ReqOrgAdmin diff --git a/pkg/services/ngalert/api/generated_base_api_provisioning.go b/pkg/services/ngalert/api/generated_base_api_provisioning.go index f6399502489..f6449e041a3 100644 --- a/pkg/services/ngalert/api/generated_base_api_provisioning.go +++ b/pkg/services/ngalert/api/generated_base_api_provisioning.go @@ -25,6 +25,7 @@ type ProvisioningApi interface { RouteDeleteTemplate(*models.ReqContext) response.Response RouteGetAlertRule(*models.ReqContext) response.Response RouteGetAlertRuleGroup(*models.ReqContext) response.Response + RouteGetAlertRules(*models.ReqContext) response.Response RouteGetContactpoints(*models.ReqContext) response.Response RouteGetMuteTiming(*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"] 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 { return f.handleRouteGetContactpoints(ctx) } @@ -245,6 +249,16 @@ func (api *API) RegisterProvisioningApiEndpoints(srv ProvisioningApi, m *metrics 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( toMacaronPath("/api/v1/provisioning/contact-points"), api.authorize(http.MethodGet, "/api/v1/provisioning/contact-points"), diff --git a/pkg/services/ngalert/api/provisioning.go b/pkg/services/ngalert/api/provisioning.go index 703a697ed31..e9f41dbe143 100644 --- a/pkg/services/ngalert/api/provisioning.go +++ b/pkg/services/ngalert/api/provisioning.go @@ -76,6 +76,10 @@ func (f *ProvisioningApiHandler) handleRouteDeleteMuteTiming(ctx *models.ReqCont 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 { return f.svc.RouteRouteGetAlertRule(ctx, UID) } diff --git a/pkg/services/ngalert/api/tooling/api.json b/pkg/services/ngalert/api/tooling/api.json index d46b63b94a1..33533e36e63 100644 --- a/pkg/services/ngalert/api/tooling/api.json +++ b/pkg/services/ngalert/api/tooling/api.json @@ -2164,6 +2164,12 @@ ], "type": "object" }, + "ProvisionedAlertRules": { + "items": { + "$ref": "#/definitions/ProvisionedAlertRule" + }, + "type": "array" + }, "PushoverConfig": { "properties": { "expire": { @@ -3077,7 +3083,6 @@ "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" @@ -3088,6 +3093,9 @@ "Host": { "type": "string" }, + "OmitHost": { + "type": "boolean" + }, "Opaque": { "type": "string" }, @@ -3110,7 +3118,7 @@ "$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" }, "Userinfo": { @@ -3395,6 +3403,7 @@ "type": "object" }, "gettableAlert": { + "description": "GettableAlert gettable alert", "properties": { "annotations": { "$ref": "#/definitions/labelSet" @@ -3513,7 +3522,6 @@ "type": "array" }, "integration": { - "description": "Integration integration", "properties": { "lastNotifyAttempt": { "description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time", @@ -3657,7 +3665,6 @@ "type": "array" }, "postableSilence": { - "description": "PostableSilence postable silence", "properties": { "comment": { "description": "comment", @@ -3815,6 +3822,21 @@ }, "paths": { "/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": { "consumes": [ "application/json" 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 8924b6cc2a5..8bf8c656a5c 100644 --- a/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go +++ b/pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go @@ -7,6 +7,13 @@ import ( "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 // // Get a specific alert rule by UID. @@ -63,6 +70,9 @@ type AlertRuleHeaders struct { XDisableProvenance string `json:"X-Disable-Provenance"` } +// swagger:model +type ProvisionedAlertRules []ProvisionedAlertRule + type ProvisionedAlertRule struct { ID int64 `json:"id"` 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 // // Get a rule group. diff --git a/pkg/services/ngalert/api/tooling/post.json b/pkg/services/ngalert/api/tooling/post.json index 09e0e92780f..1981634ab72 100644 --- a/pkg/services/ngalert/api/tooling/post.json +++ b/pkg/services/ngalert/api/tooling/post.json @@ -2164,6 +2164,12 @@ ], "type": "object" }, + "ProvisionedAlertRules": { + "items": { + "$ref": "#/definitions/ProvisionedAlertRule" + }, + "type": "array" + }, "PushoverConfig": { "properties": { "expire": { @@ -3077,7 +3083,6 @@ "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" @@ -3088,6 +3093,9 @@ "Host": { "type": "string" }, + "OmitHost": { + "type": "boolean" + }, "Opaque": { "type": "string" }, @@ -3110,7 +3118,7 @@ "$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" }, "Userinfo": { @@ -3266,7 +3274,6 @@ "type": "object" }, "alertGroup": { - "description": "AlertGroup alert group", "properties": { "alerts": { "description": "alerts", @@ -3456,7 +3463,6 @@ "type": "array" }, "gettableSilence": { - "description": "GettableSilence gettable silence", "properties": { "comment": { "description": "comment", @@ -3511,6 +3517,7 @@ "type": "array" }, "integration": { + "description": "Integration integration", "properties": { "lastNotifyAttempt": { "description": "A timestamp indicating the last attempt to deliver a notification regardless of the outcome.\nFormat: date-time", @@ -3654,7 +3661,6 @@ "type": "array" }, "postableSilence": { - "description": "PostableSilence postable silence", "properties": { "comment": { "description": "comment", @@ -5485,6 +5491,21 @@ } }, "/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": { "consumes": [ "application/json" diff --git a/pkg/services/ngalert/api/tooling/spec.json b/pkg/services/ngalert/api/tooling/spec.json index feabdcf0e78..d88fa6723ce 100644 --- a/pkg/services/ngalert/api/tooling/spec.json +++ b/pkg/services/ngalert/api/tooling/spec.json @@ -1690,6 +1690,22 @@ } }, "/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": { "consumes": [ "application/json" @@ -4611,6 +4627,12 @@ } } }, + "ProvisionedAlertRules": { + "type": "array", + "items": { + "$ref": "#/definitions/ProvisionedAlertRule" + } + }, "PushoverConfig": { "type": "object", "properties": { @@ -5524,9 +5546,8 @@ } }, "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", - "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": { "ForceQuery": { "type": "boolean" @@ -5537,6 +5558,9 @@ "Host": { "type": "string" }, + "OmitHost": { + "type": "boolean" + }, "Opaque": { "type": "string" }, @@ -5713,7 +5737,6 @@ } }, "alertGroup": { - "description": "AlertGroup alert group", "type": "object", "required": [ "alerts", @@ -5907,7 +5930,6 @@ "$ref": "#/definitions/gettableAlerts" }, "gettableSilence": { - "description": "GettableSilence gettable silence", "type": "object", "required": [ "comment", @@ -5964,6 +5986,7 @@ "$ref": "#/definitions/gettableSilences" }, "integration": { + "description": "Integration integration", "type": "object", "required": [ "name", @@ -6108,7 +6131,6 @@ } }, "postableSilence": { - "description": "PostableSilence postable silence", "type": "object", "required": [ "comment", diff --git a/pkg/services/ngalert/provisioning/alert_rules.go b/pkg/services/ngalert/provisioning/alert_rules.go index f360deed48f..8482a90730b 100644 --- a/pkg/services/ngalert/provisioning/alert_rules.go +++ b/pkg/services/ngalert/provisioning/alert_rules.go @@ -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) { query := &models.GetAlertRuleByUIDQuery{ OrgID: orgID, diff --git a/public/api-merged.json b/public/api-merged.json index 23aeab68549..469438c375e 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -2442,6 +2442,21 @@ } }, "/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": { "consumes": [ "application/json" @@ -15587,6 +15602,12 @@ } } }, + "ProvisionedAlertRules": { + "type": "array", + "items": { + "$ref": "#/definitions/ProvisionedAlertRule" + } + }, "PushoverConfig": { "type": "object", "properties": { @@ -17547,9 +17568,8 @@ "type": "string" }, "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", - "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": { "ForceQuery": { "type": "boolean" @@ -17560,6 +17580,9 @@ "Host": { "type": "string" }, + "OmitHost": { + "type": "boolean" + }, "Opaque": { "type": "string" }, @@ -18484,6 +18507,7 @@ } }, "gettableAlert": { + "description": "GettableAlert gettable alert", "type": "object", "required": [ "labels", @@ -18602,7 +18626,6 @@ } }, "integration": { - "description": "Integration integration", "type": "object", "required": [ "name", @@ -18746,7 +18769,6 @@ } }, "postableSilence": { - "description": "PostableSilence postable silence", "type": "object", "required": [ "comment",