From 68b05b8aaad30df3cc460fc46e7709cc4ec0c6d7 Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki Date: Wed, 17 Mar 2021 12:47:03 +0200 Subject: [PATCH] AlertingNG: Unified alerting API mock (#32040) * AlertingNG: Alertmanager mock API * AlertingNG: Remove permissions API routes * Add example POST payloads * Prometheus and testing mock API --- go.mod | 3 +- go.sum | 6 +- pkg/services/ngalert/api/api.go | 7 +- .../ngalert/api/api_alertmanager_base.go | 6 +- .../ngalert/api/api_alertmanager_mock.go | 891 ++++++++++++++++++ .../ngalert/api/api_permissions_base.go | 47 - .../ngalert/api/api_prometheus_mock.go | 133 +++ pkg/services/ngalert/api/api_testing_base.go | 4 +- pkg/services/ngalert/api/api_testing_mock.go | 32 + .../api/test-data/post-silence-data.json | 23 + .../api/test-data/post-user-config.json | 375 ++++++++ .../ngalert/api/test-data/test-receiver.json | 22 + .../ngalert/api/test-data/test-rule.json | 21 + pkg/services/ngalert/api/util.go | 14 + 14 files changed, 1525 insertions(+), 59 deletions(-) create mode 100644 pkg/services/ngalert/api/api_alertmanager_mock.go delete mode 100644 pkg/services/ngalert/api/api_permissions_base.go create mode 100644 pkg/services/ngalert/api/api_prometheus_mock.go create mode 100644 pkg/services/ngalert/api/api_testing_mock.go create mode 100644 pkg/services/ngalert/api/test-data/post-silence-data.json create mode 100644 pkg/services/ngalert/api/test-data/post-user-config.json create mode 100644 pkg/services/ngalert/api/test-data/test-receiver.json create mode 100644 pkg/services/ngalert/api/test-data/test-rule.json diff --git a/go.mod b/go.mod index da23458d45e..296acbf4c9d 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/go-kit/kit v0.10.0 github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07 + github.com/go-openapi/strfmt v0.20.0 github.com/go-sourcemap/sourcemap v2.1.3+incompatible github.com/go-sql-driver/mysql v1.5.0 github.com/go-stack/stack v1.8.0 @@ -39,7 +40,7 @@ require ( github.com/google/go-cmp v0.5.5 github.com/google/uuid v1.2.0 github.com/gosimple/slug v1.9.0 - github.com/grafana/alerting-api v0.0.0-20210311171115-b0eb4577f38c + github.com/grafana/alerting-api v0.0.0-20210316151414-4987b85e57ee github.com/grafana/grafana-aws-sdk v0.2.0 github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 github.com/grafana/grafana-plugin-sdk-go v0.88.0 diff --git a/go.sum b/go.sum index ee4b2b1bd6e..a9cded721ef 100644 --- a/go.sum +++ b/go.sum @@ -801,8 +801,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs= github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg= -github.com/grafana/alerting-api v0.0.0-20210311171115-b0eb4577f38c h1:xmmEjOGr87S1ZMinUTCA+ikMSLvJRrCYADZ9/ewMtWM= -github.com/grafana/alerting-api v0.0.0-20210311171115-b0eb4577f38c/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= +github.com/grafana/alerting-api v0.0.0-20210316151414-4987b85e57ee h1:LZo5p1wyV4RbQa28QBJZoS+VtnH1ruh4K4k1fOuHxJs= +github.com/grafana/alerting-api v0.0.0-20210316151414-4987b85e57ee/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= github.com/grafana/grafana v1.9.2-0.20210308201921-4ce0a49eac03/go.mod h1:AHRRvd4utJGY25J5nW8aL7wZzn/LcJ0z2za9oOp14j4= github.com/grafana/grafana-aws-sdk v0.1.0/go.mod h1:+pPo5U+pX0zWimR7YBc7ASeSQfbRkcTyQYqMiAj7G5U= github.com/grafana/grafana-aws-sdk v0.2.0 h1:UTBBYwye+ad5YUIlwN7TGxLdz1wXN3Ezhl0pseDGRVA= @@ -984,6 +984,7 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.2.1 h1:S/EaQvW6FpWMYAvYvY+OBDvpaM+izu0oiwo5y0MH7U0= github.com/jonboulle/clockwork v0.2.1/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -1417,6 +1418,7 @@ github.com/prometheus/prometheus v1.8.2-0.20210217141258-a6be548dbc17 h1:VN3p3Nb github.com/prometheus/prometheus v1.8.2-0.20210217141258-a6be548dbc17/go.mod h1:dv3B1syqmkrkmo665MPCU6L8PbTXIiUeg/OEQULLNxA= github.com/prometheus/statsd_exporter v0.15.0/go.mod h1:Dv8HnkoLQkeEjkIE4/2ndAA7WL1zHKK7WMqFQqu72rw= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3 h1:eL7x4/zMnlquMxYe7V078BD7MGskZ0daGln+SJCVzuY= github.com/quasilyte/go-ruleguard/dsl/fluent v0.0.0-20201222093424-5d7e62a465d3/go.mod h1:P7JlQWFT7jDcFZMtUPQbtGzzzxva3rBn6oIF+LPwFcM= github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= diff --git a/pkg/services/ngalert/api/api.go b/pkg/services/ngalert/api/api.go index d1672db485f..48194fd8b41 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -41,11 +41,10 @@ type API struct { // RegisterAPIEndpoints registers API handlers func (api *API) RegisterAPIEndpoints() { logger := log.New("ngalert.api") - api.RegisterAlertmanagerApiEndpoints(AlertmanagerApiBase{log: logger}) - api.RegisterPermissionsApiEndpoints(PermissionsApiBase{log: logger}) - api.RegisterPrometheusApiEndpoints(PrometheusApiBase{log: logger}) + api.RegisterAlertmanagerApiEndpoints(AlertmanagerApiMock{log: logger}) + api.RegisterPrometheusApiEndpoints(PrometheusApiMock{log: logger}) api.RegisterRulerApiEndpoints(RulerApiMock{log: logger}) - api.RegisterTestingApiEndpoints(TestingApiBase{log: logger}) + api.RegisterTestingApiEndpoints(TestingApiMock{log: logger}) // Legacy routes; they will be removed in v8 api.RouteRegister.Group("/api/alert-definitions", func(alertDefinitions routing.RouteRegister) { diff --git a/pkg/services/ngalert/api/api_alertmanager_base.go b/pkg/services/ngalert/api/api_alertmanager_base.go index 319b1b670fc..b8ecd0d4bd0 100644 --- a/pkg/services/ngalert/api/api_alertmanager_base.go +++ b/pkg/services/ngalert/api/api_alertmanager_base.go @@ -26,7 +26,7 @@ type AlertmanagerApiService interface { RouteGetAmAlerts(*models.ReqContext) response.Response RouteGetSilence(*models.ReqContext) response.Response RouteGetSilences(*models.ReqContext) response.Response - RoutePostAlertingConfig(*models.ReqContext, apimodels.UserConfig) response.Response + RoutePostAlertingConfig(*models.ReqContext, apimodels.PostableUserConfig) response.Response RoutePostAmAlerts(*models.ReqContext, apimodels.PostableAlerts) response.Response } @@ -44,7 +44,7 @@ func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) { group.Get(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/alerts"), routing.Wrap(srv.RouteGetAmAlerts)) group.Get(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteGetSilence)) group.Get(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/silences"), routing.Wrap(srv.RouteGetSilences)) - group.Post(toMacaronPath("/alertmanager/{DatasourceId}/config/api/v1/alerts"), binding.Bind(apimodels.UserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) + group.Post(toMacaronPath("/alertmanager/{DatasourceId}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) group.Post(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAmAlerts)) }) } @@ -102,7 +102,7 @@ func (base AlertmanagerApiBase) RouteGetSilences(c *models.ReqContext) response. return response.Error(http.StatusNotImplemented, "", nil) } -func (base AlertmanagerApiBase) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.UserConfig) response.Response { +func (base AlertmanagerApiBase) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response { datasourceId := c.Params(":DatasourceId") base.log.Info("RoutePostAlertingConfig: ", "DatasourceId", datasourceId) base.log.Info("RoutePostAlertingConfig: ", "body", body) diff --git a/pkg/services/ngalert/api/api_alertmanager_mock.go b/pkg/services/ngalert/api/api_alertmanager_mock.go new file mode 100644 index 00000000000..05cdcc1ff89 --- /dev/null +++ b/pkg/services/ngalert/api/api_alertmanager_mock.go @@ -0,0 +1,891 @@ +/*Package api contains mock API implementation of unified alerting + * + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + * + * Need to remove unused imports. + */ +package api + +import ( + "net/http" + "time" + + "github.com/grafana/grafana/pkg/api/dtos" + + "github.com/grafana/grafana/pkg/components/securejsondata" + "github.com/grafana/grafana/pkg/components/simplejson" + + "github.com/go-openapi/strfmt" + apimodels "github.com/grafana/alerting-api/pkg/api" + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/util" + amv2 "github.com/prometheus/alertmanager/api/v2/models" + "github.com/prometheus/alertmanager/config" +) + +func toSimpleJSON(blob string) *simplejson.Json { + json, _ := simplejson.NewJson([]byte(blob)) + return json +} + +var alertmanagerReceiver = models.AlertNotification{ + Id: 1, + Uid: "alertmanager UID", + OrgId: 1, + Name: "an alert manager receiver", + Type: "prometheus-alertmanager", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "basicAuthUser": "user", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://localhost:9093" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "basicAuthPassword": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var alertmanagerReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&alertmanagerReceiver)) + +var dingdingReceiver = models.AlertNotification{ + Id: 2, + Uid: "dingding UID", + OrgId: 1, + Name: "a dingding receiver", + Type: "dingding", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "msgType": "link", + "severity": "critical", + "uploadImage": false, + "url": "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var dingdingReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&dingdingReceiver)) + +var discordReceiver = models.AlertNotification{ + Id: 3, + Uid: "discord UID", + OrgId: 1, + Name: "a discord receiver", + Type: "discord", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "content": "@user", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var discordReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&discordReceiver)) + +var emailReceiver = models.AlertNotification{ + Id: 4, + Uid: "email UID", + OrgId: 1, + Name: "an email receiver", + Type: "email", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "addresses": "", + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "singleEmail": true, + "uploadImage": false + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var emailReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&emailReceiver)) + +var googlechatReceiver = models.AlertNotification{ + Id: 5, + Uid: "googlechatReceiver UID", + OrgId: 1, + Name: "a googlechat receiver", + Type: "googlechat", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var googlechatReceiverDTOs = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&googlechatReceiver)) + +var hipchatReceiver = models.AlertNotification{ + Id: 6, + Uid: "hipchat UID", + OrgId: 1, + Name: "a hipchat receiver", + Type: "hipchat", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "apiKey": "", + "autoResolve": true, + "httpMethod": "POST", + "roomid": "12345", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var hipchatReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&hipchatReceiver)) + +var kafkaReceiver = models.AlertNotification{ + Id: 7, + Uid: "kafka UID", + OrgId: 1, + Name: "a kafka receiver", + Type: "kafka", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "kafkaRestProxy": "http://localhost:8082", + "kafkaTopic": "topic1", + "severity": "critical", + "uploadImage": false + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var kafkaReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&kafkaReceiver)) + +var lineReceiver = models.AlertNotification{ + Id: 8, + Uid: "line UID", + OrgId: 1, + Name: "a line receiver", + Type: "line", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(` "settings": { + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false + },`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "token": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var lineReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&lineReceiver)) + +var opsgenieReceiver = models.AlertNotification{ + Id: 9, + Uid: "opsgenie UID", + OrgId: 1, + Name: "a opsgenie receiver", + Type: "opsgenie", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(` "settings": { + "apiUrl": "https://api.opsgenie.com/v2/alerts", + "autoClose": true, + "autoResolve": true, + "httpMethod": "POST", + "overridePriority": true, + "severity": "critical", + "uploadImage": false + },`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "apiKey": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var opsgenieReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&opsgenieReceiver)) + +var pagerdutyReceiver = models.AlertNotification{ + Id: 10, + Uid: "pagerduty UID", + OrgId: 1, + Name: "a pagerduty receiver", + Type: "pagerduty", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": true + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "integrationKey": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var pagerdutyReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&pagerdutyReceiver)) + +var pushoverReceiver = models.AlertNotification{ + Id: 11, + Uid: "pushover UID", + OrgId: 1, + Name: "a pushover receiver", + Type: "pushover", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "apiToken": "", + "autoResolve": true, + "device": "", + "expire": "", + "httpMethod": "POST", + "okPriority": "0", + "okSound": "cosmic", + "priority": "1", + "retry": "30", + "severity": "critical", + "sound": "pushover", + "uploadImage": true, + "userKey": "" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "apiToken": "", + "userKey": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var pushoverReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&pushoverReceiver)) + +var sensuReceiver = models.AlertNotification{ + Id: 12, + Uid: "sensu UID", + OrgId: 1, + Name: "a sensu receiver", + Type: "sensu", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "handler": "", + "httpMethod": "POST", + "severity": "critical", + "source": "", + "uploadImage": false, + "url": "http://sensu-api.local:4567/results", + "username": "" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var sensuReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&sensuReceiver)) + +var sensugoReceiver = models.AlertNotification{ + Id: 13, + Uid: "sensugo UID", + OrgId: 1, + Name: "a sensugo receiver", + Type: "sensugo", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "check": "", + "entity": "", + "handler": "", + "httpMethod": "POST", + "namespace": "", + "severity": "critical", + "uploadImage": false, + "url": "http://sensu-api.local:8080" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "apikey": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var sensugoReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&sensugoReceiver)) + +var slackReceiver = models.AlertNotification{ + Id: 14, + Uid: "slack UID", + OrgId: 1, + Name: "a slack receiver", + Type: "slack", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "iconEmoji": "", + "iconUrl": "", + "mentionGroups": "", + "mentionUsers": "", + "recipient": "", + "severity": "critical", + "uploadImage": false, + "username": "" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "token": "", + "url": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var slackReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&slackReceiver)) + +var teamsReceiver = models.AlertNotification{ + Id: 15, + Uid: "teams UID", + OrgId: 1, + Name: "a teams receiver", + Type: "teams", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var teamsReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&teamsReceiver)) + +var telegramReceiver = models.AlertNotification{ + Id: 16, + Uid: "telegram UID", + OrgId: 1, + Name: "a telegram receiver", + Type: "telegram", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "chatid": "12345", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "bottoken": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var telegramReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&telegramReceiver)) + +var threemaReceiver = models.AlertNotification{ + Id: 17, + Uid: "threema UID", + OrgId: 1, + Name: "a threema receiver", + Type: "threema", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "gateway_id": "*3MAGWID", + "httpMethod": "POST", + "recipient_id": "YOUR3MID", + "severity": "critical", + "uploadImage": false + },`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "api_secret": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var threemaDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&threemaReceiver)) + +var victoropsReceiver = models.AlertNotification{ + Id: 18, + Uid: "victorops UID", + OrgId: 1, + Name: "a victorops receiver", + Type: "victorops", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`{ + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{}), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var victoropsReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&victoropsReceiver)) + +var webhookReceiver = models.AlertNotification{ + Id: 19, + Uid: "webhook UID", + OrgId: 1, + Name: "a webhook receiver", + Type: "webhook", + SendReminder: false, + DisableResolveMessage: false, + Frequency: 5 * time.Minute, + IsDefault: false, + Settings: toSimpleJSON(`x{ + "autoResolve": true, + "httpMethod": "POST", + "password": "", + "severity": "critical", + "uploadImage": true, + "url": "http://localhost:3010", + "username": "" + }`), + SecureSettings: securejsondata.GetEncryptedJsonData(map[string]string{ + "password": "", + }), + Created: time.Now().Add(-time.Hour), + Updated: time.Now().Add(-5 * time.Minute), +} +var webhookReceiverDTO = apimodels.GettableGrafanaReceiver(*dtos.NewAlertNotification(&webhookReceiver)) + +type AlertmanagerApiMock struct { + log log.Logger +} + +func (mock AlertmanagerApiMock) RouteCreateSilence(c *models.ReqContext, body apimodels.SilenceBody) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteCreateSilence: ", "DatasourceId", datasourceID) + mock.log.Info("RouteCreateSilence: ", "body", body) + return response.JSON(http.StatusAccepted, util.DynMap{"message": "silence created"}) +} + +func (mock AlertmanagerApiMock) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteDeleteAlertingConfig: ", "DatasourceId", datasourceID) + return response.JSON(http.StatusOK, util.DynMap{"message": "config deleted"}) +} + +func (mock AlertmanagerApiMock) RouteDeleteSilence(c *models.ReqContext) response.Response { + silenceID := c.Params(":SilenceId") + mock.log.Info("RouteDeleteSilence: ", "SilenceId", silenceID) + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteDeleteSilence: ", "DatasourceId", datasourceID) + return response.JSON(http.StatusOK, util.DynMap{"message": "silence deleted"}) +} + +func (mock AlertmanagerApiMock) RouteGetAlertingConfig(c *models.ReqContext) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteGetAlertingConfig: ", "DatasourceId", datasourceID) + // now := time.Now() + result := apimodels.GettableUserConfig{ + TemplateFiles: map[string]string{ + "tmpl1": "val1", + "tmpl2": "val2", + }, + AlertmanagerConfig: apimodels.GettableApiAlertingConfig{ + Config: config.Config{ + Global: &config.GlobalConfig{}, + Route: &config.Route{}, + InhibitRules: []*config.InhibitRule{}, + Receivers: []*config.Receiver{}, + Templates: []string{}, + }, + Receivers: []*apimodels.GettableApiReceiver{ + { + GettableGrafanaReceivers: apimodels.GettableGrafanaReceivers{ + GrafanaManagedReceivers: []*apimodels.GettableGrafanaReceiver{ + &alertmanagerReceiverDTO, + &dingdingReceiverDTO, + &discordReceiverDTO, + &emailReceiverDTO, + &googlechatReceiverDTOs, + &hipchatReceiverDTO, + &kafkaReceiverDTO, + &lineReceiverDTO, + &opsgenieReceiverDTO, + &pagerdutyReceiverDTO, + &pushoverReceiverDTO, + &sensuReceiverDTO, + &sensugoReceiverDTO, + &slackReceiverDTO, + &teamsReceiverDTO, + &telegramReceiverDTO, + &threemaDTO, + &victoropsReceiverDTO, + &webhookReceiverDTO, + }, + }, + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock AlertmanagerApiMock) RouteGetAmAlertGroups(c *models.ReqContext) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteGetAmAlertGroups: ", "DatasourceId", datasourceID) + now := time.Now() + result := apimodels.AlertGroups{ + &amv2.AlertGroup{ + Alerts: []*amv2.GettableAlert{ + { + Annotations: amv2.LabelSet{ + "annotation1-1": "value1", + "annotation1-2": "value2", + }, + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + Fingerprint: stringPtr("fingerprint 1"), + Receivers: []*amv2.Receiver{ + { + Name: stringPtr("receiver identifier 1-1"), + }, + { + Name: stringPtr("receiver identifier 1-2"), + }, + }, + StartsAt: timePtr(strfmt.DateTime(now)), + Status: &amv2.AlertStatus{ + InhibitedBy: []string{"inhibitedBy 1"}, + SilencedBy: []string{"silencedBy 1"}, + State: stringPtr(amv2.AlertStatusStateActive), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Alert: amv2.Alert{ + GeneratorURL: strfmt.URI("a URL"), + Labels: amv2.LabelSet{ + "label1-1": "value1", + "label1-2": "value2", + }, + }, + }, + { + Annotations: amv2.LabelSet{ + "annotation2-1": "value1", + "annotation2-2": "value2", + }, + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + Fingerprint: stringPtr("fingerprint 2"), + Receivers: []*amv2.Receiver{ + { + Name: stringPtr("receiver identifier 2-1"), + }, + { + Name: stringPtr("receiver identifier 2-2"), + }, + }, + StartsAt: timePtr(strfmt.DateTime(now)), + Status: &amv2.AlertStatus{ + InhibitedBy: []string{"inhibitedBy 2"}, + SilencedBy: []string{"silencedBy 2"}, + State: stringPtr(amv2.AlertStatusStateActive), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Alert: amv2.Alert{ + GeneratorURL: strfmt.URI("a URL"), + Labels: amv2.LabelSet{ + "label2-1": "value1", + "label2-2": "value2", + }, + }, + }, + }, + Labels: amv2.LabelSet{ + "label1-1": "value1", + "label1-2": "value2", + }, + Receiver: &amv2.Receiver{ + Name: stringPtr("receiver identifier 2-1"), + }, + }, + &amv2.AlertGroup{ + Alerts: []*amv2.GettableAlert{ + { + Annotations: amv2.LabelSet{ + "annotation2-1": "value1", + "annotation2-2": "value2", + }, + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + Fingerprint: stringPtr("fingerprint 2"), + Receivers: []*amv2.Receiver{ + { + Name: stringPtr("receiver identifier 2-1"), + }, + { + Name: stringPtr("receiver identifier 2-2"), + }, + }, + StartsAt: timePtr(strfmt.DateTime(now)), + Status: &amv2.AlertStatus{ + InhibitedBy: []string{"inhibitedBy 2"}, + SilencedBy: []string{"silencedBy 2"}, + State: stringPtr(amv2.AlertStatusStateActive), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Alert: amv2.Alert{ + GeneratorURL: strfmt.URI("a URL"), + Labels: amv2.LabelSet{ + "label2-1": "value1", + "label2-2": "value2", + }, + }, + }, + }, + Labels: amv2.LabelSet{ + "label2-1": "value1", + "label2-2": "value2", + }, + Receiver: &amv2.Receiver{ + Name: stringPtr("receiver identifier 2-1"), + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock AlertmanagerApiMock) RouteGetAmAlerts(c *models.ReqContext) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteGetAmAlerts: ", "DatasourceId", datasourceID) + now := time.Now() + result := apimodels.GettableAlerts{ + &amv2.GettableAlert{ + Annotations: amv2.LabelSet{ + "annotation1-1": "value1", + "annotation1-2": "value2", + }, + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + Fingerprint: stringPtr("fingerprint 1"), + Receivers: []*amv2.Receiver{ + { + Name: stringPtr("receiver identifier 1-1"), + }, + { + Name: stringPtr("receiver identifier 1-2"), + }, + }, + StartsAt: timePtr(strfmt.DateTime(now)), + Status: &amv2.AlertStatus{ + InhibitedBy: []string{"inhibitedBy 1"}, + SilencedBy: []string{"silencedBy 1"}, + State: stringPtr(amv2.AlertStatusStateActive), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Alert: amv2.Alert{ + GeneratorURL: strfmt.URI("a URL"), + Labels: amv2.LabelSet{ + "label1-1": "value1", + "label1-2": "value2", + }, + }, + }, + &amv2.GettableAlert{ + Annotations: amv2.LabelSet{ + "annotation2-1": "value1", + "annotation2-2": "value2", + }, + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + Fingerprint: stringPtr("fingerprint 2"), + Receivers: []*amv2.Receiver{ + { + Name: stringPtr("receiver identifier 2-1"), + }, + { + Name: stringPtr("receiver identifier 2-2"), + }, + }, + StartsAt: timePtr(strfmt.DateTime(now)), + Status: &amv2.AlertStatus{ + InhibitedBy: []string{"inhibitedBy 2"}, + SilencedBy: []string{"silencedBy 2"}, + State: stringPtr(amv2.AlertStatusStateActive), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Alert: amv2.Alert{ + GeneratorURL: strfmt.URI("a URL"), + Labels: amv2.LabelSet{ + "label2-1": "value1", + "label2-2": "value2", + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock AlertmanagerApiMock) RouteGetSilence(c *models.ReqContext) response.Response { + silenceID := c.Params(":SilenceId") + mock.log.Info("RouteGetSilence: ", "SilenceId", silenceID) + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteGetSilence: ", "DatasourceId", datasourceID) + now := time.Now() + result := apimodels.GettableSilence{ + ID: stringPtr("id"), + Status: &amv2.SilenceStatus{ + State: stringPtr("active"), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Silence: amv2.Silence{ + Comment: stringPtr("comment"), + CreatedBy: stringPtr("created by"), + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + StartsAt: timePtr(strfmt.DateTime(now)), + Matchers: []*amv2.Matcher{ + { + IsRegex: boolPtr(false), + Name: stringPtr("name"), + Value: stringPtr("value"), + }, + { + IsRegex: boolPtr(false), + Name: stringPtr("name2"), + Value: stringPtr("value2"), + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock AlertmanagerApiMock) RouteGetSilences(c *models.ReqContext) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RouteGetSilences: ", "DatasourceId", datasourceID) + now := time.Now() + result := apimodels.GettableSilences{ + &amv2.GettableSilence{ + ID: stringPtr("silence1"), + Status: &amv2.SilenceStatus{ + State: stringPtr("active"), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Silence: amv2.Silence{ + Comment: stringPtr("silence1 comment"), + CreatedBy: stringPtr("silence1 created by"), + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + StartsAt: timePtr(strfmt.DateTime(now)), + Matchers: []*amv2.Matcher{ + { + IsRegex: boolPtr(false), + Name: stringPtr("silence1 name"), + Value: stringPtr("silence1 value"), + }, + { + IsRegex: boolPtr(true), + Name: stringPtr("silence1 name2"), + Value: stringPtr("silence1 value2"), + }, + }, + }, + }, + &amv2.GettableSilence{ + ID: stringPtr("silence2"), + Status: &amv2.SilenceStatus{ + State: stringPtr("pending"), + }, + UpdatedAt: timePtr(strfmt.DateTime(now.Add(-time.Hour))), + Silence: amv2.Silence{ + Comment: stringPtr("silence2 comment"), + CreatedBy: stringPtr("silence2 created by"), + EndsAt: timePtr(strfmt.DateTime(now.Add(time.Hour))), + StartsAt: timePtr(strfmt.DateTime(now)), + Matchers: []*amv2.Matcher{ + { + IsRegex: boolPtr(false), + Name: stringPtr("silence2 name"), + Value: stringPtr("silence2 value"), + }, + { + IsRegex: boolPtr(true), + Name: stringPtr("silence2 name2"), + Value: stringPtr("silence2 value2"), + }, + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock AlertmanagerApiMock) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RoutePostAlertingConfig: ", "DatasourceId", datasourceID) + mock.log.Info("RoutePostAlertingConfig: ", "body", body) + return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"}) +} + +func (mock AlertmanagerApiMock) RoutePostAmAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response { + datasourceID := c.Params(":DatasourceId") + mock.log.Info("RoutePostAmAlerts: ", "DatasourceId", datasourceID) + mock.log.Info("RoutePostAmAlerts: ", "body", body) + return response.JSON(http.StatusOK, util.DynMap{"message": "alerts created"}) +} diff --git a/pkg/services/ngalert/api/api_permissions_base.go b/pkg/services/ngalert/api/api_permissions_base.go deleted file mode 100644 index 2225c328f30..00000000000 --- a/pkg/services/ngalert/api/api_permissions_base.go +++ /dev/null @@ -1,47 +0,0 @@ -/*Package api contains base API implementation of unified alerting - * - * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) - * - * Need to remove unused imports. - */ -package api - -import ( - "net/http" - - "github.com/go-macaron/binding" - apimodels "github.com/grafana/alerting-api/pkg/api" - "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/models" -) - -type PermissionsApiService interface { - RouteGetNamespacePermissions(*models.ReqContext) response.Response - RouteSetNamespacePermissions(*models.ReqContext, apimodels.Permissions) response.Response -} - -type PermissionsApiBase struct { - log log.Logger -} - -func (api *API) RegisterPermissionsApiEndpoints(srv PermissionsApiService) { - api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Get(toMacaronPath("/api/v1/namespace/{Namespace}/permissions"), routing.Wrap(srv.RouteGetNamespacePermissions)) - group.Post(toMacaronPath("/api/v1/namespace/{Namespace}/permissions"), binding.Bind(apimodels.Permissions{}), routing.Wrap(srv.RouteSetNamespacePermissions)) - }) -} - -func (base PermissionsApiBase) RouteGetNamespacePermissions(c *models.ReqContext) response.Response { - namespace := c.Params(":Namespace") - base.log.Info("RouteGetNamespacePermissions: ", "Namespace", namespace) - return response.Error(http.StatusNotImplemented, "", nil) -} - -func (base PermissionsApiBase) RouteSetNamespacePermissions(c *models.ReqContext, body apimodels.Permissions) response.Response { - namespace := c.Params(":Namespace") - base.log.Info("RouteSetNamespacePermissions: ", "Namespace", namespace) - base.log.Info("RouteSetNamespacePermissions: ", "body", body) - return response.Error(http.StatusNotImplemented, "", nil) -} diff --git a/pkg/services/ngalert/api/api_prometheus_mock.go b/pkg/services/ngalert/api/api_prometheus_mock.go new file mode 100644 index 00000000000..92bd80ab6e5 --- /dev/null +++ b/pkg/services/ngalert/api/api_prometheus_mock.go @@ -0,0 +1,133 @@ +package api + +import ( + "net/http" + "time" + + apimodels "github.com/grafana/alerting-api/pkg/api" + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" +) + +type PrometheusApiMock struct { + log log.Logger +} + +func (mock PrometheusApiMock) RouteGetAlertStatuses(c *models.ReqContext) response.Response { + datasourceId := c.Params(":DatasourceId") + mock.log.Info("RouteGetAlertStatuses: ", "DatasourceId", datasourceId) + now := time.Now() + result := apimodels.AlertResponse{ + Data: apimodels.AlertDiscovery{ + Alerts: []*apimodels.Alert{ + { + Labels: map[string]string{ + "label 1": "value 1", + "label 2": "value 2", + }, + Annotations: map[string]string{ + "alert annotation 1": "value 1", + "alert annotation 2": "value 2", + }, + State: "firing", + ActiveAt: &now, + Value: "", + }, + { + Labels: map[string]string{ + "label 1-2": "value 1", + "label 2-2": "value 2", + }, + Annotations: map[string]string{ + "alert annotation 1-2": "value 1", + "alert annotation 2-2": "value 2", + }, + State: "inactive", + ActiveAt: &now, + Value: "", + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} + +func (mock PrometheusApiMock) RouteGetRuleStatuses(c *models.ReqContext) response.Response { + datasourceId := c.Params(":DatasourceId") + mock.log.Info("RouteGetRuleStatuses: ", "DatasourceId", datasourceId) + now := time.Now() + result := apimodels.RuleResponse{ + Data: apimodels.RuleDiscovery{ + RuleGroups: []*apimodels.RuleGroup{ + { + Name: "group1", + Interval: 60, + LastEvaluation: now.Add(-time.Minute), + EvaluationTime: 1, + Rules: []apimodels.AlertingRule{ + { + State: "firing", + Name: "rule 1-1", + Query: `{ + "title": "rule 1-1", + "condition": "A", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "type": "math", + "expression": "2 + 2 > 1" + } + } + ], + "no_data_state": "NoData", + "exec_err_state": "Alerting" + }`, + Duration: 600, + Annotations: map[string]string{ + "annotation 1": "value 1", + "annotation 2": "value 2", + }, + Alerts: []*apimodels.Alert{ + { + Labels: map[string]string{ + "label 1": "value 1", + "label 2": "value 2", + }, + Annotations: map[string]string{ + "alert annotation 1": "value 1", + "alert annotation 2": "value 2", + }, + State: "firing", + ActiveAt: &now, + Value: "", + }, + { + Labels: map[string]string{ + "label 1-2": "value 1", + "label 2-2": "value 2", + }, + Annotations: map[string]string{ + "alert annotation 1-2": "value 1", + "alert annotation 2-2": "value 2", + }, + State: "inactive", + ActiveAt: &now, + Value: "", + }, + }, + }, + }, + }, + }, + }, + } + return response.JSON(http.StatusOK, result) +} diff --git a/pkg/services/ngalert/api/api_testing_base.go b/pkg/services/ngalert/api/api_testing_base.go index 716a7d119d0..3b367700cea 100644 --- a/pkg/services/ngalert/api/api_testing_base.go +++ b/pkg/services/ngalert/api/api_testing_base.go @@ -28,8 +28,8 @@ type TestingApiBase struct { func (api *API) RegisterTestingApiEndpoints(srv TestingApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Get(toMacaronPath("/api/v1/receiver/test"), binding.Bind(apimodels.ExtendedReceiver{}), routing.Wrap(srv.RouteTestReceiverConfig)) - group.Get(toMacaronPath("/api/v1/rule/test"), binding.Bind(apimodels.TestRulePayload{}), routing.Wrap(srv.RouteTestRuleConfig)) + 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)) }) } diff --git a/pkg/services/ngalert/api/api_testing_mock.go b/pkg/services/ngalert/api/api_testing_mock.go new file mode 100644 index 00000000000..75058b4111e --- /dev/null +++ b/pkg/services/ngalert/api/api_testing_mock.go @@ -0,0 +1,32 @@ +package api + +import ( + "net/http" + + apimodels "github.com/grafana/alerting-api/pkg/api" + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/util" +) + +type TestingApiMock struct { + log log.Logger +} + +func (mock TestingApiMock) RouteTestReceiverConfig(c *models.ReqContext, body apimodels.ExtendedReceiver) response.Response { + mock.log.Info("RouteTestReceiverConfig: ", "body", body) + return response.JSON(http.StatusOK, util.DynMap{"message": "success"}) +} + +func (mock TestingApiMock) RouteTestRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload) response.Response { + mock.log.Info("RouteTestRuleConfig: ", "body", body) + result := apimodels.TestRuleResponse{ + GrafanaAlertInstances: apimodels.AlertInstancesResponse{ + Instances: [][]byte{ + []byte("QVJST1cxAAD/////+AAAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFAAAAACAAAAKAAAAAQAAACE////CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAKT///8IAAAADAAAAAAAAAAAAAAABAAAAG5hbWUAAAAAAQAAABgAAAAAABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAAQAAAAEQAAAAAAAAGQAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAAAAAAEAAQABAAAAAAAAAAAAAAA/////4gAAAAUAAAAAAAAAAwAFgAUABMADAAEAAwAAAAIAAAAAAAAABQAAAAAAAADAwAKABgADAAIAAQACgAAABQAAAA4AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAQAAAADAAUABIADAAIAAQADAAAABAAAAAsAAAAPAAAAAAAAwABAAAACAEAAAAAAACQAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABQAAAAAgAAACgAAAAEAAAAhP///wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAACk////CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAEAAAAYAAAAAAASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEAAAABEAAAAAAAABkAAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAMAAAAAAAAAAAAAAAEAAAAbmFtZQAAAAAAAAAABAAEAAQAAAAAAAAAAAAAACgBAABBUlJPVzE="), + }, + }, + } + return response.JSON(http.StatusOK, result) +} diff --git a/pkg/services/ngalert/api/test-data/post-silence-data.json b/pkg/services/ngalert/api/test-data/post-silence-data.json new file mode 100644 index 00000000000..d6034b9521c --- /dev/null +++ b/pkg/services/ngalert/api/test-data/post-silence-data.json @@ -0,0 +1,23 @@ +{ + "id": "id", + "status": { + "state": "active" + }, + "updatedAt": "2021-03-11T13:50:42.218+02:00", + "comment": "comment", + "createdBy": "created by", + "endsAt": "2021-03-11T15:50:42.218+02:00", + "matchers": [ + { + "isRegex": false, + "name": "name", + "value": "value" + }, + { + "isRegex": false, + "name": "name2", + "value": "value2" + } + ], + "startsAt": "2021-03-11T14:50:42.218+02:00" +} \ No newline at end of file diff --git a/pkg/services/ngalert/api/test-data/post-user-config.json b/pkg/services/ngalert/api/test-data/post-user-config.json new file mode 100644 index 00000000000..a845a34fbc3 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/post-user-config.json @@ -0,0 +1,375 @@ +{ + "template_files": { + "tmpl1": "val1", + "tmpl2": "val2" + }, + "alertmanager_config": { + "global": { + "resolve_timeout": "1s", + "smtp_smarthost": "" + }, + "route": {}, + "templates": [], + "receivers": [ + { + "name": "", + "grafana_managed_receiver_configs": [ + { + "uid": "alertmanager UID", + "name": "an alert manager receiver", + "type": "prometheus-alertmanager", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "basicAuthUser": "user", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://localhost:9093" + }, + "secureSettings": { + "basicAuthPassword": "" + } + }, + { + "uid": "dingding UID", + "name": "a dingding receiver", + "type": "dingding", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "msgType": "link", + "severity": "critical", + "uploadImage": false, + "url": "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx" + }, + "secureSettings": {} + }, + { + "uid": "discord UID", + "name": "a discord receiver", + "type": "discord", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "content": "@user", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }, + "secureSettings": {} + }, + { + "uid": "email UID", + "name": "an email receiver", + "type": "email", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "addresses": "", + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "singleEmail": true, + "uploadImage": false + }, + "secureSettings": {} + }, + { + "uid": "googlechatReceiver UID", + "name": "a googlechat receiver", + "type": "googlechat", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }, + "secureSettings": {} + }, + { + "uid": "hipchat UID", + "name": "a hipchat receiver", + "type": "hipchat", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "apiKey": "", + "autoResolve": true, + "httpMethod": "POST", + "roomid": "12345", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }, + "secureSettings": {} + }, + { + "uid": "kafka UID", + "name": "a kafka receiver", + "type": "kafka", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "kafkaRestProxy": "http://localhost:8082", + "kafkaTopic": "topic1", + "severity": "critical", + "uploadImage": false + }, + "secureSettings": {} + }, + { + "uid": "line UID", + "name": "a line receiver", + "type": "line", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": "settings", + "secureSettings": { + "token": "" + } + }, + { + "uid": "opsgenie UID", + "name": "a opsgenie receiver", + "type": "opsgenie", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": "settings", + "secureSettings": { + "apiKey": "" + } + }, + { + "uid": "pagerduty UID", + "name": "a pagerduty receiver", + "type": "pagerduty", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "severity": "critic* Connection #0 to host localhost left intact\nal", + "uploadImage": true + }, + "secureSettings": { + "integrationKey": "" + } + }, + { + "uid": "pushover UID", + "name": "a pushover receiver", + "type": "pushover", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "apiToken": "", + "autoResolve": true, + "device": "", + "expire": "", + "httpMethod": "POST", + "okPriority": "0", + "okSound": "cosmic", + "priority": "1", + "retry": "30", + "severity": "critical", + "sound": "pushover", + "uploadImage": true, + "userKey": "" + }, + "secureSettings": { + "apiToken": "", + "userKey": "" + } + }, + { + "uid": "sensu UID", + "name": "a sensu receiver", + "type": "sensu", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "handler": "", + "httpMethod": "POST", + "severity": "critical", + "source": "", + "uploadImage": false, + "url": "http://sensu-api.local:4567/results", + "username": "" + }, + "secureSettings": {} + }, + { + "uid": "sensugo UID", + "name": "a sensugo receiver", + "type": "sensugo", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "check": "", + "entity": "", + "handler": "", + "httpMethod": "POST", + "namespace": "", + "severity": "critical", + "uploadImage": false, + "url": "http://sensu-api.local:8080" + }, + "secureSettings": { + "apikey": "" + } + }, + { + "uid": "slack UID", + "name": "a slack receiver", + "type": "slack", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "iconEmoji": "", + "iconUrl": "", + "mentionGroups": "", + "mentionUsers": "", + "recipient": "", + "severity": "critical", + "uploadImage": false, + "username": "" + }, + "secureSettings": { + "token": "", + "url": "" + } + }, + { + "uid": "teams UID", + "name": "a teams receiver", + "type": "teams", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }, + "secureSettings": {} + }, + { + "uid": "telegram UID", + "name": "a telegram receiver", + "type": "telegram", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "chatid": "12345", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false + }, + "secureSettings": { + "bottoken": "" + } + }, + { + "uid": "threema UID", + "name": "a threema receiver", + "type": "threema", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "gateway_id": "*3MAGWID", + "httpMethod": "POST", + "recipient_id": "YOUR3MID", + "severity": "critical", + "uploadImage": false + }, + "secureSettings": { + "api_secret": "" + } + }, + { + "uid": "victorops UID", + "name": "a victorops receiver", + "type": "victorops", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://" + }, + "secureSettings": {} + }, + { + "uid": "webhook UID", + "name": "a webhook receiver", + "type": "webhook", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": null, + "secureSettings": { + "password": "" + } + } + ] + } + ] + } +} \ No newline at end of file diff --git a/pkg/services/ngalert/api/test-data/test-receiver.json b/pkg/services/ngalert/api/test-data/test-receiver.json new file mode 100644 index 00000000000..d3741c8e031 --- /dev/null +++ b/pkg/services/ngalert/api/test-data/test-receiver.json @@ -0,0 +1,22 @@ +{ + "grafana_managed_receiver": { + "uid": "alertmanager UID", + "name": "an alert manager receiver", + "type": "prometheus-alertmanager", + "isDefault": false, + "sendReminder": false, + "disableResolveMessage": false, + "frequency": "5m", + "settings": { + "autoResolve": true, + "basicAuthUser": "user", + "httpMethod": "POST", + "severity": "critical", + "uploadImage": false, + "url": "http://localhost:9093" + }, + "secureSettings": { + "basicAuthPassword": "" + } + } +} \ No newline at end of file diff --git a/pkg/services/ngalert/api/test-data/test-rule.json b/pkg/services/ngalert/api/test-data/test-rule.json new file mode 100644 index 00000000000..61cd8240cae --- /dev/null +++ b/pkg/services/ngalert/api/test-data/test-rule.json @@ -0,0 +1,21 @@ +{ + "grafana_condition": { + "condition": "A", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 18000, + "to": 10800 + }, + "model": { + "datasource": "__expr__", + "type": "math", + "expression": "2 + 2 > 1" + } + } + ], + "now": "2021-03-11T14:50:42.218+02:00" + } +} \ No newline at end of file diff --git a/pkg/services/ngalert/api/util.go b/pkg/services/ngalert/api/util.go index f5418cc9232..263ec981d1c 100644 --- a/pkg/services/ngalert/api/util.go +++ b/pkg/services/ngalert/api/util.go @@ -3,6 +3,8 @@ package api import ( "fmt" "regexp" + + "github.com/go-openapi/strfmt" ) var searchRegex = regexp.MustCompile(`\{(\w+)\}`) @@ -13,3 +15,15 @@ func toMacaronPath(path string) string { return []byte(fmt.Sprintf(":%s", m)) })) } + +func timePtr(t strfmt.DateTime) *strfmt.DateTime { + return &t +} + +func stringPtr(s string) *string { + return &s +} + +func boolPtr(b bool) *bool { + return &b +}