diff --git a/pkg/services/ngalert/api/api_alertmanager_base.go b/pkg/services/ngalert/api/api_alertmanager_base.go index f327ddf1d13..9d70c87c45d 100644 --- a/pkg/services/ngalert/api/api_alertmanager_base.go +++ b/pkg/services/ngalert/api/api_alertmanager_base.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" ) @@ -36,17 +37,17 @@ type AlertmanagerApiBase struct { func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.PostableSilence{}), routing.Wrap(srv.RouteCreateSilence)) - group.Delete(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig)) - group.Delete(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence)) - group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups)) - group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts)) - group.Get(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig)) - group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence)) - group.Get(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences)) - group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts)) - group.Post(toMacaronPath("/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) - }) + group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.PostableSilence{}), routing.Wrap(srv.RouteCreateSilence)) + group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig)) + group.Delete(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence)) + group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAMAlertGroups)) + group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), routing.Wrap(srv.RouteGetAMAlerts)) + group.Get(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig)) + group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence)) + group.Get(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences)) + group.Post(toMacaronPath("/api/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAMAlerts)) + group.Post(toMacaronPath("/api/alertmanager/{Recipient}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) + }, middleware.ReqSignedIn) } func (base AlertmanagerApiBase) RouteCreateSilence(c *models.ReqContext, body apimodels.PostableSilence) response.Response { diff --git a/pkg/services/ngalert/api/api_prometheus_base.go b/pkg/services/ngalert/api/api_prometheus_base.go index 3ddb7e35d7c..fd4e123d847 100644 --- a/pkg/services/ngalert/api/api_prometheus_base.go +++ b/pkg/services/ngalert/api/api_prometheus_base.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" ) @@ -26,9 +27,9 @@ type PrometheusApiBase struct { func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Get(toMacaronPath("/prometheus/{Recipient}/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertStatuses)) - group.Get(toMacaronPath("/prometheus/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRuleStatuses)) - }) + group.Get(toMacaronPath("/api/prometheus/{Recipient}/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertStatuses)) + group.Get(toMacaronPath("/api/prometheus/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRuleStatuses)) + }, middleware.ReqSignedIn) } func (base PrometheusApiBase) RouteGetAlertStatuses(c *models.ReqContext) response.Response { diff --git a/pkg/services/ngalert/api/api_ruler_base.go b/pkg/services/ngalert/api/api_ruler_base.go index c7c6e46b276..aed73088e6b 100644 --- a/pkg/services/ngalert/api/api_ruler_base.go +++ b/pkg/services/ngalert/api/api_ruler_base.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" ) @@ -32,13 +33,13 @@ type RulerApiBase struct { func (api *API) RegisterRulerApiEndpoints(srv RulerApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Delete(toMacaronPath("/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteDeleteNamespaceRulesConfig)) - group.Delete(toMacaronPath("/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteDeleteRuleGroupConfig)) - group.Get(toMacaronPath("/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteGetNamespaceRulesConfig)) - group.Get(toMacaronPath("/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteGetRulegGroupConfig)) - group.Get(toMacaronPath("/ruler/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRulesConfig)) - group.Post(toMacaronPath("/ruler/{Recipient}/api/v1/rules/{Namespace}"), binding.Bind(apimodels.PostableRuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig)) - }) + group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteDeleteNamespaceRulesConfig)) + group.Delete(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteDeleteRuleGroupConfig)) + group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteGetNamespaceRulesConfig)) + group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteGetRulegGroupConfig)) + group.Get(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRulesConfig)) + group.Post(toMacaronPath("/api/ruler/{Recipient}/api/v1/rules/{Namespace}"), binding.Bind(apimodels.PostableRuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig)) + }, middleware.ReqSignedIn) } func (base RulerApiBase) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) response.Response { diff --git a/pkg/services/ngalert/api/api_testing_base.go b/pkg/services/ngalert/api/api_testing_base.go index 3b367700cea..1b3357ff582 100644 --- a/pkg/services/ngalert/api/api_testing_base.go +++ b/pkg/services/ngalert/api/api_testing_base.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" ) @@ -30,7 +31,7 @@ func (api *API) RegisterTestingApiEndpoints(srv TestingApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { group.Post(toMacaronPath("/api/v1/receiver/test"), binding.Bind(apimodels.ExtendedReceiver{}), routing.Wrap(srv.RouteTestReceiverConfig)) group.Post(toMacaronPath("/api/v1/rule/test"), binding.Bind(apimodels.TestRulePayload{}), routing.Wrap(srv.RouteTestRuleConfig)) - }) + }, middleware.ReqSignedIn) } func (base TestingApiBase) RouteTestReceiverConfig(c *models.ReqContext, body apimodels.ExtendedReceiver) response.Response { diff --git a/pkg/services/ngalert/api/swagger-codegen/templates/controller-api.mustache b/pkg/services/ngalert/api/swagger-codegen/templates/controller-api.mustache index fdc02b23122..3a5a96231f0 100644 --- a/pkg/services/ngalert/api/swagger-codegen/templates/controller-api.mustache +++ b/pkg/services/ngalert/api/swagger-codegen/templates/controller-api.mustache @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/infra/log" apimodels "github.com/grafana/alerting-api/pkg/api" + "github.com/grafana/grafana/pkg/middleware" ) type {{classname}}Service interface { {{#operation}} @@ -24,7 +25,7 @@ type {{classname}}Base struct { func (api *API) Register{{classname}}Endpoints(srv {{classname}}Service) { api.RouteRegister.Group("", func(group routing.RouteRegister){ {{#operations}}{{#operation}} group.{{httpMethod}}(toMacaronPath("{{{path}}}"){{#bodyParams}}, binding.Bind(apimodels.{{dataType}}{}){{/bodyParams}}, routing.Wrap(srv.{{nickname}})){{/operation}}{{/operations}} - }) + }, middleware.ReqSignedIn) }{{#operation}} diff --git a/pkg/services/ngalert/api/test-data/am-alertmanager-recipient-unauthorized.http b/pkg/services/ngalert/api/test-data/am-alertmanager-recipient-unauthorized.http new file mode 100644 index 00000000000..07ef355bd79 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/am-alertmanager-recipient-unauthorized.http @@ -0,0 +1,101 @@ +@alertManagerDatasourceID = 36 + +### +# create AM configuration +POST http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts +content-type: application/json + +{ + "template_files": {}, + "alertmanager_config": { + "global": { + "smtp_smarthost": "localhost:25", + "smtp_from": "youraddress@example.org", + }, + "receivers": [ + { + "name": "example-email", + "email_configs": { + "to": "youraddress@example.org" + } + } + ], + "route": { + "receiver": "example-email", + "routes": [], + "group_wait": "1s", + "group_interval": "1s", + "repeat_interval": "1s" + }, + "template": [] + } +} + +### +# get latest AM configuration +GET http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts +content-type: application/json + +### +# delete AM configuration +DELETE http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts + +### +# create AM alerts +POST http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts +content-type: application/json + +### +# get silences - no silences +GET http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo" + +### +# create silence +POST http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences +content-type: application/json + +{ + "comment": "string", + "createdBy": "string", + "endsAt": "2023-03-31T14:17:04.419Z", + "matchers": [ + { + "isRegex": true, + "name": "string", + "value": "string" + } + ], + "startsAt": "2021-03-31T13:17:04.419Z" +} + +### +# update silence - does not exist +POST http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences +content-type: application/json + +{ + "id": "something", + "comment": "string", + "createdBy": "string", + "endsAt": "2023-03-31T14:17:04.419Z", + "matchers": [ + { + "isRegex": true, + "name": "string", + "value": "string" + } + ], + "startsAt": "2021-03-31T13:17:04.419Z" +} + +### +# get silences +GET http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences + +### +# get silence +GET http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown + +### +# delete silence +DELETE http://localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown \ No newline at end of file diff --git a/pkg/services/ngalert/api/test-data/am-grafana-recipient-unauthorized.http b/pkg/services/ngalert/api/test-data/am-grafana-recipient-unauthorized.http new file mode 100644 index 00000000000..d82a444b40b --- /dev/null +++ b/pkg/services/ngalert/api/test-data/am-grafana-recipient-unauthorized.http @@ -0,0 +1,96 @@ +@grafana = grafana + +### +# create AM configuration +POST http://localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts +content-type: application/json + +< ./post-user-config.json + +### +# get latest AM configuration +GET http://localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts +content-type: application/json + +### +# delete AM configuration +DELETE http://localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts + +### +# create AM alerts +POST http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/alerts +content-type: application/json + +### +# get silences - no silences +GET http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo" + +### +# create silence +POST http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences +content-type: application/json + +{ + "comment": "string", + "createdBy": "string", + "endsAt": "2023-03-31T14:17:04.419Z", + "matchers": [ + { + "isRegex": true, + "name": "string", + "value": "string" + } + ], + "startsAt": "2021-03-31T13:17:04.419Z" +} + +### +# update silence - does not exist +POST http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences +content-type: application/json + +{ + "id": "something", + "comment": "string", + "createdBy": "string", + "endsAt": "2023-03-31T14:17:04.419Z", + "matchers": [ + { + "isRegex": true, + "name": "string", + "value": "string" + } + ], + "startsAt": "2021-03-31T13:17:04.419Z" +} + +### +# create silence - bad paylaad - start time must be before end time +POST http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences +content-type: application/json + +{ + "comment": "string", + "createdBy": "string", + "endsAt": "2019-03-31T14:17:04.419Z", + "matchers": [ + { + "isRegex": true, + "name": "string", + "value": "string" + } + ], + "startsAt": "2021-03-31T13:17:04.419Z" +} + +### +# get silences +GET http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences + +### +# get silence - unknown +GET http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/unknown + +### +# delete silence - unknown +DELETE http://localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/unknown diff --git a/pkg/services/ngalert/api/test-data/am.http b/pkg/services/ngalert/api/test-data/am-grafana-recipient.http similarity index 50% rename from pkg/services/ngalert/api/test-data/am.http rename to pkg/services/ngalert/api/test-data/am-grafana-recipient.http index c2261bf78c0..3fff2ca569e 100644 --- a/pkg/services/ngalert/api/test-data/am.http +++ b/pkg/services/ngalert/api/test-data/am-grafana-recipient.http @@ -1,42 +1,33 @@ -@lokiDatasourceID = 32 -@prometheusDatasourceID = 875 @grafana = grafana -# unsupported loki backend -GET http://admin:admin@localhost:3000/alertmanager/{{lokiDatasourceID}}/config/api/v1/alerts - -### -# unsupported cortex backend -GET http://admin:admin@localhost:3000/alertmanager/{{prometheusDatasourceID}}/config/api/v1/alerts - ### # create AM configuration -POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts +POST http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts content-type: application/json < ./post-user-config.json ### # get latest AM configuration -GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts +GET http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts content-type: application/json ### # delete AM configuration -DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts +DELETE http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/config/api/v1/alerts ### # create AM alerts -POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/alerts +POST http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/alerts content-type: application/json ### # get silences - no silences -GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo" +GET http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo" ### # create silence -POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences +POST http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences content-type: application/json { @@ -55,7 +46,7 @@ content-type: application/json ### # update silence - does not exist -POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences +POST http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences content-type: application/json { @@ -75,7 +66,7 @@ content-type: application/json ### # create silence - bad paylaad - start time must be before end time -POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences +POST http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences content-type: application/json { @@ -95,24 +86,23 @@ content-type: application/json ### # get silences # @name getSilences -GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences - +GET http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silences ### @silenceID = {{getSilences.response.body.$.[0].id}} ### # get silence -GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}} +GET http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}} ### # get silence - unknown -GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/unknown +GET http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/unknown ### # delete silence -DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}} +DELETE http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/{{silenceID}} ### # delete silence - unknown -DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silence/unknown +DELETE http://admin:admin@localhost:3000/api/alertmanager/{{grafana}}/api/v2/silence/unknown diff --git a/pkg/services/ngalert/api/test-data/am-unsupported-backends.http b/pkg/services/ngalert/api/test-data/am-unsupported-backends.http new file mode 100644 index 00000000000..9a3a2c0f3fd --- /dev/null +++ b/pkg/services/ngalert/api/test-data/am-unsupported-backends.http @@ -0,0 +1,13 @@ +@lokiDatasourceID = 32 +@prometheusDatasourceID = 35 + +# unsupported loki backend +GET http://admin:admin@localhost:3000/api/alertmanager/{{lokiDatasourceID}}/config/api/v1/alerts + +### +# unsupported cortex backend +GET http://admin:admin@localhost:3000/api/alertmanager/{{prometheusDatasourceID}}/config/api/v1/alerts + +### +# unknown datasource +GET http://admin:admin@localhost:3000/api/alertmanager/unknown/config/api/v1/alerts \ No newline at end of file diff --git a/pkg/services/ngalert/api/test-data/ruler-cortex-recipient.http b/pkg/services/ngalert/api/test-data/ruler-cortex-recipient.http new file mode 100644 index 00000000000..f0dda6c2f67 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/ruler-cortex-recipient.http @@ -0,0 +1,75 @@ +@prometheusDatasourceID = 35 + +// should point to an existing folder named alerting +@namespace1 = test + +// create/update test namespace group42 rulegroup +POST http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "1m" + } + ] +} + +### +// create group101 +POST http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group101", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "2m" + } + ] +} + +### +// get group42 rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get group101 rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group101 + +### +// get namespace rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get org rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules + +### +// delete group42 rules +DELETE http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules - only group101 should be listed +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// delete namespace rules +DELETE http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get namespace rules - no rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get group42 rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules +GET http://admin:admin@localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} diff --git a/pkg/services/ngalert/api/test-data/ruler-grafana-recipient-unauthorized.http b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient-unauthorized.http new file mode 100644 index 00000000000..c23f6754d19 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient-unauthorized.http @@ -0,0 +1,242 @@ +@grafanaRecipient = grafana + +// should point to an existing folder named alerting +@namespace1 = alerting + +// create group42 under unknown namespace - it should fail +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/unknown +content-type: application/json + +< ./post-rulegroup-42.json + +### +// create group42 +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +content-type: application/json + +< ./post-rulegroup-42.json + +### +// create group101 +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +content-type: application/json + +< ./post-rulegroup-101.json + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 + +### +// get group101 rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group101 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} + +### +// get org rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules + +### +// delete group42 rules +DELETE http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules - only group101 should be listed +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} + +### +// delete namespace rules +DELETE http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} + +### +// get namespace rules - no rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} + +### +// recreate group42 +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "interval": "10s", + "rules": [ + { + "grafana_alert": { + "title": "prom query with SSE", + "condition": "condition", + "data": [ + { + "refId": "query", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "gdev-prometheus", + "datasourceUid": "000000002", + "expr": "http_request_duration_microseconds_count", + "hide": false, + "interval": "", + "intervalMs": 1000, + "legendFormat": "", + "maxDataPoints": 100, + "refId": "query" + } + }, + { + "refId": "reduced", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "datasourceUid": "-100", + "expression": "query", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "reducer": "mean", + "refId": "reduced", + "type": "reduce" + } + }, + { + "refId": "condition", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "datasourceUid": "-100", + "expression": "$reduced > 10", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "refId": "condition", + "type": "math" + } + } + ], + "no_data_state": "NoData", + "exec_err_state": "Alerting" + } + } + ] +} + + +### +// get group42 rules +# @name getRule +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 + +### +// update group42 interval and condition threshold +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "interval": "20s", + "rules": [ + { + "grafana_alert": { + "title": "prom query with SSE", + "condition": "condition", + "uid": "UID", + "data": [ + { + "refId": "query", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "gdev-prometheus", + "datasourceUid": "000000002", + "expr": "http_request_duration_microseconds_count", + "hide": false, + "interval": "", + "intervalMs": 1000, + "legendFormat": "", + "maxDataPoints": 100, + "refId": "query" + } + }, + { + "refId": "reduced", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "datasourceUid": "-100", + "expression": "query", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "reducer": "mean", + "refId": "reduced", + "type": "reduce" + } + }, + { + "refId": "condition", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "datasourceUid": "-100", + "expression": "$reduced > 42", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 100, + "refId": "condition", + "type": "math" + } + } + ], + "no_data_state": "NoData", + "exec_err_state": "Alerting" + } + } + ] +} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 + +### +// update group42 - delete all rules +POST http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "interval": "20s", + "rules": [] +} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} diff --git a/pkg/services/ngalert/api/test-data/ruler.http b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http similarity index 73% rename from pkg/services/ngalert/api/test-data/ruler.http rename to pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http index 576a012d6f7..412a7bad270 100644 --- a/pkg/services/ngalert/api/test-data/ruler.http +++ b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http @@ -4,60 +4,60 @@ @namespace1 = alerting // create group42 under unknown namespace - it should fail -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/unknown +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/unknown content-type: application/json < ./post-rulegroup-42.json ### // create group42 -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} content-type: application/json < ./post-rulegroup-42.json ### // create group101 -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} content-type: application/json < ./post-rulegroup-101.json ### // get group42 rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 ### // get group101 rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group101 +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group101 ### // get namespace rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} ### // get org rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules ### // delete group42 rules -DELETE http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 +DELETE http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 ### // get namespace rules - only group101 should be listed -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} ### // delete namespace rules -DELETE http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +DELETE http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} ### // get namespace rules - no rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} ### // recreate group42 -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} content-type: application/json { @@ -137,7 +137,7 @@ content-type: application/json ### // get group42 rules # @name getRule -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 ### @ruleUID = {{getRule.response.body.$.rules[0].grafana_alert.uid}} @@ -145,7 +145,7 @@ GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{ ### // update group42 interval and condition threshold -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} content-type: application/json { @@ -224,11 +224,11 @@ content-type: application/json ### // get group42 rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 ### // update group42 - delete all rules -POST http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} content-type: application/json { @@ -239,8 +239,8 @@ content-type: application/json ### // get group42 rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}}/group42 ### // get namespace rules -GET http://admin:admin@localhost:3000/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} +GET http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/{{namespace1}} diff --git a/pkg/services/ngalert/api/test-data/ruler-loki-recipient.http b/pkg/services/ngalert/api/test-data/ruler-loki-recipient.http new file mode 100644 index 00000000000..8fa3ef1c8f4 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/ruler-loki-recipient.http @@ -0,0 +1,75 @@ +@lokiDatasourceID = 32 + +// should point to an existing folder named alerting +@namespace1 = test + +// create/update test namespace group42 rulegroup +POST http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "1m" + } + ] +} + +### +// create group101 +POST http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group101", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "2m" + } + ] +} + +### +// get group42 rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get group101 rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group101 + +### +// get namespace rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get org rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules + +### +// delete group42 rules +DELETE http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules - only group101 should be listed +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// delete namespace rules +DELETE http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get namespace rules - no rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get group42 rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules +GET http://admin:admin@localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} diff --git a/pkg/services/ngalert/api/test-data/ruler-lotex-recipient-unauthorized.http b/pkg/services/ngalert/api/test-data/ruler-lotex-recipient-unauthorized.http new file mode 100644 index 00000000000..e5e9812d831 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/ruler-lotex-recipient-unauthorized.http @@ -0,0 +1,148 @@ +@lokiDatasourceID = 32 +@prometheusDatasourceID = 35 + +// should point to an existing folder named alerting +@namespace1 = test + +// create/update test namespace group42 rulegroup +POST http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "1m" + } + ] +} + +### +// create group101 +POST http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group101", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "2m" + } + ] +} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get group101 rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group101 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get org rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules + +### +// delete group42 rules +DELETE http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules - only group101 should be listed +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// delete namespace rules +DELETE http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get namespace rules - no rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{lokiDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// create/update test namespace group42 rulegroup +POST http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group42", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "1m" + } + ] +} + +### +// create group101 +POST http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} +content-type: application/json + +{ + "name": "group101", + "rules": [ + { + "alert": "logs_exist", + "expr": "rate({cluster=\"us-central1\", job=\"loki-prod/loki-canary\"}[1m]) > 0", + "for": "2m" + } + ] +} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get group101 rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group101 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get org rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules + +### +// delete group42 rules +DELETE http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules - only group101 should be listed +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// delete namespace rules +DELETE http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get namespace rules - no rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} + +### +// get group42 rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}}/group42 + +### +// get namespace rules +GET http://localhost:3000/api/ruler/{{prometheusDatasourceID}}/api/v1/rules/{{namespace1}} \ No newline at end of file