diff --git a/go.mod b/go.mod index 9df54157652..2fbae33f9b6 100644 --- a/go.mod +++ b/go.mod @@ -40,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-20210318231719-9499804fc548 + github.com/grafana/alerting-api v0.0.0-20210323142651-d6515052e2f0 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 eaf2883f403..afcd9ff4b00 100644 --- a/go.sum +++ b/go.sum @@ -796,6 +796,10 @@ 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-20210318231719-9499804fc548 h1:KjyaZJhPJ15Ul/+OQr8mbO7kDpU5i7G3r5FGVZKClTQ= github.com/grafana/alerting-api v0.0.0-20210318231719-9499804fc548/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= +github.com/grafana/alerting-api v0.0.0-20210323141138-8873de5bf07a h1:OGKDRdmQSXKFJelrJUf9O8Xh0C8u+OQG1NSurcBYpOI= +github.com/grafana/alerting-api v0.0.0-20210323141138-8873de5bf07a/go.mod h1:5IppnPguSHcCbVLGCVzVjBvuQZNbYgVJ4KyXXjhCyWY= +github.com/grafana/alerting-api v0.0.0-20210323142651-d6515052e2f0 h1:bMYGd71RigZvkLmcdedGdMDJXKJ20luqEQLbqjgAAjI= +github.com/grafana/alerting-api v0.0.0-20210323142651-d6515052e2f0/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= @@ -1411,6 +1415,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 3b8f8946c06..541e0400ac7 100644 --- a/pkg/services/ngalert/api/api.go +++ b/pkg/services/ngalert/api/api.go @@ -46,6 +46,7 @@ func (api *API) RegisterAPIEndpoints() { api.RegisterAlertmanagerApiEndpoints(AlertmanagerApiMock{log: logger}) api.RegisterPrometheusApiEndpoints(PrometheusApiMock{log: logger}) api.RegisterRulerApiEndpoints(NewForkedRuler( + api.DatasourceCache, &LotexRuler{DataProxy: api.DataProxy, log: logger}, RulerApiMock{log: logger}, )) diff --git a/pkg/services/ngalert/api/api_alertmanager_base.go b/pkg/services/ngalert/api/api_alertmanager_base.go index b8ecd0d4bd0..2ac1048a32c 100644 --- a/pkg/services/ngalert/api/api_alertmanager_base.go +++ b/pkg/services/ngalert/api/api_alertmanager_base.go @@ -36,82 +36,82 @@ type AlertmanagerApiBase struct { func (api *API) RegisterAlertmanagerApiEndpoints(srv AlertmanagerApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Post(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/silences"), binding.Bind(apimodels.SilenceBody{}), routing.Wrap(srv.RouteCreateSilence)) - group.Delete(toMacaronPath("/alertmanager/{DatasourceId}/config/api/v1/alerts"), routing.Wrap(srv.RouteDeleteAlertingConfig)) - group.Delete(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/silence/{SilenceId}"), routing.Wrap(srv.RouteDeleteSilence)) - group.Get(toMacaronPath("/alertmanager/{DatasourceId}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig)) - group.Get(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/alerts/groups"), routing.Wrap(srv.RouteGetAmAlertGroups)) - 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.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) - group.Post(toMacaronPath("/alertmanager/{DatasourceId}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAmAlerts)) + group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/silences"), binding.Bind(apimodels.SilenceBody{}), 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}/config/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertingConfig)) + 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}/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}/config/api/v1/alerts"), binding.Bind(apimodels.PostableUserConfig{}), routing.Wrap(srv.RoutePostAlertingConfig)) + group.Post(toMacaronPath("/alertmanager/{Recipient}/api/v2/alerts"), binding.Bind(apimodels.PostableAlerts{}), routing.Wrap(srv.RoutePostAmAlerts)) }) } func (base AlertmanagerApiBase) RouteCreateSilence(c *models.ReqContext, body apimodels.SilenceBody) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteCreateSilence: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteCreateSilence: ", "Recipient", recipient) base.log.Info("RouteCreateSilence: ", "body", body) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteDeleteAlertingConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteDeleteAlertingConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteDeleteSilence(c *models.ReqContext) response.Response { silenceId := c.Params(":SilenceId") base.log.Info("RouteDeleteSilence: ", "SilenceId", silenceId) - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteDeleteSilence: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteDeleteSilence: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteGetAlertingConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetAlertingConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetAlertingConfig: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteGetAmAlertGroups(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetAmAlertGroups: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetAmAlertGroups: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteGetAmAlerts(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetAmAlerts: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetAmAlerts: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteGetSilence(c *models.ReqContext) response.Response { silenceId := c.Params(":SilenceId") base.log.Info("RouteGetSilence: ", "SilenceId", silenceId) - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetSilence: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetSilence: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RouteGetSilences(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetSilences: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetSilences: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RoutePostAlertingConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RoutePostAlertingConfig: ", "Recipient", recipient) base.log.Info("RoutePostAlertingConfig: ", "body", body) return response.Error(http.StatusNotImplemented, "", nil) } func (base AlertmanagerApiBase) RoutePostAmAlerts(c *models.ReqContext, body apimodels.PostableAlerts) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RoutePostAmAlerts: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RoutePostAmAlerts: ", "Recipient", recipient) base.log.Info("RoutePostAmAlerts: ", "body", body) return response.Error(http.StatusNotImplemented, "", nil) } diff --git a/pkg/services/ngalert/api/api_alertmanager_mock.go b/pkg/services/ngalert/api/api_alertmanager_mock.go index 05cdcc1ff89..27f57ea31a1 100644 --- a/pkg/services/ngalert/api/api_alertmanager_mock.go +++ b/pkg/services/ngalert/api/api_alertmanager_mock.go @@ -524,29 +524,29 @@ type AlertmanagerApiMock struct { } func (mock AlertmanagerApiMock) RouteCreateSilence(c *models.ReqContext, body apimodels.SilenceBody) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteCreateSilence: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteCreateSilence: ", "Recipient", recipient) 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) + recipient := c.Params(":Recipient") + mock.log.Info("RouteDeleteAlertingConfig: ", "Recipient", recipient) 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) + recipient := c.Params(":Recipient") + mock.log.Info("RouteDeleteSilence: ", "Recipient", recipient) 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) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetAlertingConfig: ", "Recipient", recipient) // now := time.Now() result := apimodels.GettableUserConfig{ TemplateFiles: map[string]string{ @@ -554,7 +554,7 @@ func (mock AlertmanagerApiMock) RouteGetAlertingConfig(c *models.ReqContext) res "tmpl2": "val2", }, AlertmanagerConfig: apimodels.GettableApiAlertingConfig{ - Config: config.Config{ + Config: apimodels.Config{ Global: &config.GlobalConfig{}, Route: &config.Route{}, InhibitRules: []*config.InhibitRule{}, @@ -594,8 +594,8 @@ func (mock AlertmanagerApiMock) RouteGetAlertingConfig(c *models.ReqContext) res } func (mock AlertmanagerApiMock) RouteGetAmAlertGroups(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetAmAlertGroups: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetAmAlertGroups: ", "Recipient", recipient) now := time.Now() result := apimodels.AlertGroups{ &amv2.AlertGroup{ @@ -715,8 +715,8 @@ func (mock AlertmanagerApiMock) RouteGetAmAlertGroups(c *models.ReqContext) resp } func (mock AlertmanagerApiMock) RouteGetAmAlerts(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetAmAlerts: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetAmAlerts: ", "Recipient", recipient) now := time.Now() result := apimodels.GettableAlerts{ &amv2.GettableAlert{ @@ -786,8 +786,8 @@ func (mock AlertmanagerApiMock) RouteGetAmAlerts(c *models.ReqContext) response. 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) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetSilence: ", "Recipient", recipient) now := time.Now() result := apimodels.GettableSilence{ ID: stringPtr("id"), @@ -818,8 +818,8 @@ func (mock AlertmanagerApiMock) RouteGetSilence(c *models.ReqContext) response.R } func (mock AlertmanagerApiMock) RouteGetSilences(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetSilences: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetSilences: ", "Recipient", recipient) now := time.Now() result := apimodels.GettableSilences{ &amv2.GettableSilence{ @@ -877,15 +877,15 @@ func (mock AlertmanagerApiMock) RouteGetSilences(c *models.ReqContext) response. } func (mock AlertmanagerApiMock) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RoutePostAlertingConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RoutePostAlertingConfig: ", "Recipient", recipient) 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) + recipient := c.Params(":Recipient") + mock.log.Info("RoutePostAmAlerts: ", "Recipient", recipient) mock.log.Info("RoutePostAmAlerts: ", "body", body) return response.JSON(http.StatusOK, util.DynMap{"message": "alerts created"}) } diff --git a/pkg/services/ngalert/api/api_prometheus_base.go b/pkg/services/ngalert/api/api_prometheus_base.go index a91a2b6eba8..3ddb7e35d7c 100644 --- a/pkg/services/ngalert/api/api_prometheus_base.go +++ b/pkg/services/ngalert/api/api_prometheus_base.go @@ -26,19 +26,19 @@ type PrometheusApiBase struct { func (api *API) RegisterPrometheusApiEndpoints(srv PrometheusApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Get(toMacaronPath("/prometheus/{DatasourceId}/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertStatuses)) - group.Get(toMacaronPath("/prometheus/{DatasourceId}/api/v1/rules"), routing.Wrap(srv.RouteGetRuleStatuses)) + group.Get(toMacaronPath("/prometheus/{Recipient}/api/v1/alerts"), routing.Wrap(srv.RouteGetAlertStatuses)) + group.Get(toMacaronPath("/prometheus/{Recipient}/api/v1/rules"), routing.Wrap(srv.RouteGetRuleStatuses)) }) } func (base PrometheusApiBase) RouteGetAlertStatuses(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetAlertStatuses: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetAlertStatuses: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base PrometheusApiBase) RouteGetRuleStatuses(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetRuleStatuses: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetRuleStatuses: ", "Recipient", recipient) 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 index 92bd80ab6e5..52c3717665e 100644 --- a/pkg/services/ngalert/api/api_prometheus_mock.go +++ b/pkg/services/ngalert/api/api_prometheus_mock.go @@ -15,8 +15,8 @@ type PrometheusApiMock struct { } func (mock PrometheusApiMock) RouteGetAlertStatuses(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - mock.log.Info("RouteGetAlertStatuses: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetAlertStatuses: ", "Recipient", recipient) now := time.Now() result := apimodels.AlertResponse{ Data: apimodels.AlertDiscovery{ @@ -54,8 +54,8 @@ func (mock PrometheusApiMock) RouteGetAlertStatuses(c *models.ReqContext) respon } func (mock PrometheusApiMock) RouteGetRuleStatuses(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - mock.log.Info("RouteGetRuleStatuses: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetRuleStatuses: ", "Recipient", recipient) now := time.Now() result := apimodels.RuleResponse{ Data: apimodels.RuleDiscovery{ diff --git a/pkg/services/ngalert/api/api_ruler_base.go b/pkg/services/ngalert/api/api_ruler_base.go index d4f2f532141..07c1769bbc0 100644 --- a/pkg/services/ngalert/api/api_ruler_base.go +++ b/pkg/services/ngalert/api/api_ruler_base.go @@ -32,26 +32,26 @@ type RulerApiBase struct { func (api *API) RegisterRulerApiEndpoints(srv RulerApiService) { api.RouteRegister.Group("", func(group routing.RouteRegister) { - group.Delete(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteDeleteNamespaceRulesConfig)) - group.Delete(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteDeleteRuleGroupConfig)) - group.Get(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules/{Namespace}"), routing.Wrap(srv.RouteGetNamespaceRulesConfig)) - group.Get(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules/{Namespace}/{Groupname}"), routing.Wrap(srv.RouteGetRulegGroupConfig)) - group.Get(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules"), routing.Wrap(srv.RouteGetRulesConfig)) - group.Post(toMacaronPath("/ruler/{DatasourceId}/api/v1/rules/{Namespace}"), binding.Bind(apimodels.RuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig)) + 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.RuleGroupConfig{}), routing.Wrap(srv.RoutePostNameRulesConfig)) }) } func (base RulerApiBase) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteDeleteNamespaceRulesConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteDeleteNamespaceRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") base.log.Info("RouteDeleteNamespaceRulesConfig: ", "Namespace", namespace) return response.Error(http.StatusNotImplemented, "", nil) } func (base RulerApiBase) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteDeleteRuleGroupConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteDeleteRuleGroupConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") base.log.Info("RouteDeleteRuleGroupConfig: ", "Namespace", namespace) groupname := c.Params(":Groupname") @@ -60,16 +60,16 @@ func (base RulerApiBase) RouteDeleteRuleGroupConfig(c *models.ReqContext) respon } func (base RulerApiBase) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetNamespaceRulesConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetNamespaceRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") base.log.Info("RouteGetNamespaceRulesConfig: ", "Namespace", namespace) return response.Error(http.StatusNotImplemented, "", nil) } func (base RulerApiBase) RouteGetRulegGroupConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetRulegGroupConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetRulegGroupConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") base.log.Info("RouteGetRulegGroupConfig: ", "Namespace", namespace) groupname := c.Params(":Groupname") @@ -78,14 +78,14 @@ func (base RulerApiBase) RouteGetRulegGroupConfig(c *models.ReqContext) response } func (base RulerApiBase) RouteGetRulesConfig(c *models.ReqContext) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RouteGetRulesConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RouteGetRulesConfig: ", "Recipient", recipient) return response.Error(http.StatusNotImplemented, "", nil) } func (base RulerApiBase) RoutePostNameRulesConfig(c *models.ReqContext, body apimodels.RuleGroupConfig) response.Response { - datasourceId := c.Params(":DatasourceId") - base.log.Info("RoutePostNameRulesConfig: ", "DatasourceId", datasourceId) + recipient := c.Params(":Recipient") + base.log.Info("RoutePostNameRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") base.log.Info("RoutePostNameRulesConfig: ", "Namespace", namespace) base.log.Info("RoutePostNameRulesConfig: ", "body", body) diff --git a/pkg/services/ngalert/api/api_ruler_mock.go b/pkg/services/ngalert/api/api_ruler_mock.go index 7e890ff486c..1e78b56af89 100644 --- a/pkg/services/ngalert/api/api_ruler_mock.go +++ b/pkg/services/ngalert/api/api_ruler_mock.go @@ -119,16 +119,16 @@ type RulerApiMock struct { } func (mock RulerApiMock) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteDeleteNamespaceRulesConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteDeleteNamespaceRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") mock.log.Info("RouteDeleteNamespaceRulesConfig: ", "Namespace", namespace) return response.JSON(http.StatusAccepted, util.DynMap{"message": "namespace rules deleted"}) } func (mock RulerApiMock) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteDeleteRuleGroupConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteDeleteRuleGroupConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") mock.log.Info("RouteDeleteRuleGroupConfig: ", "Namespace", namespace) groupname := c.Params(":Groupname") @@ -137,8 +137,8 @@ func (mock RulerApiMock) RouteDeleteRuleGroupConfig(c *models.ReqContext) respon } func (mock RulerApiMock) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetNamespaceRulesConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetNamespaceRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") mock.log.Info("RouteGetNamespaceRulesConfig: ", "Namespace", namespace) result := apimodels.NamespaceConfigResponse{ @@ -181,8 +181,8 @@ func (mock RulerApiMock) RouteGetNamespaceRulesConfig(c *models.ReqContext) resp } func (mock RulerApiMock) RouteGetRulegGroupConfig(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetRulegGroupConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetRulegGroupConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") mock.log.Info("RouteGetRulegGroupConfig: ", "Namespace", namespace) groupname := c.Params(":Groupname") @@ -212,8 +212,8 @@ func (mock RulerApiMock) RouteGetRulegGroupConfig(c *models.ReqContext) response } func (mock RulerApiMock) RouteGetRulesConfig(c *models.ReqContext) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RouteGetRulesConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RouteGetRulesConfig: ", "Recipient", recipient) result := apimodels.NamespaceConfigResponse{ "namespace1": []apimodels.RuleGroupConfig{ { @@ -286,8 +286,8 @@ func (mock RulerApiMock) RouteGetRulesConfig(c *models.ReqContext) response.Resp } func (mock RulerApiMock) RoutePostNameRulesConfig(c *models.ReqContext, body apimodels.RuleGroupConfig) response.Response { - datasourceID := c.Params(":DatasourceId") - mock.log.Info("RoutePostNameRulesConfig: ", "DatasourceId", datasourceID) + recipient := c.Params(":Recipient") + mock.log.Info("RoutePostNameRulesConfig: ", "Recipient", recipient) namespace := c.Params(":Namespace") mock.log.Info("RoutePostNameRulesConfig: ", "Namespace", namespace) mock.log.Info("RoutePostNameRulesConfig: ", "body", body) diff --git a/pkg/services/ngalert/api/fork_ruler.go b/pkg/services/ngalert/api/fork_ruler.go index 5487fcb643a..b6fcea69d9a 100644 --- a/pkg/services/ngalert/api/fork_ruler.go +++ b/pkg/services/ngalert/api/fork_ruler.go @@ -2,31 +2,52 @@ package api import ( "fmt" + "strconv" apimodels "github.com/grafana/alerting-api/pkg/api" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/datasources" ) // ForkedRuler will validate and proxy requests to the correct backend type depending on the datasource. type ForkedRuler struct { LotexRuler, GrafanaRuler RulerApiService + DatasourceCache datasources.CacheService } -func NewForkedRuler(lotex, grafana RulerApiService) *ForkedRuler { +func NewForkedRuler(datasourceCache datasources.CacheService, lotex, grafana RulerApiService) *ForkedRuler { return &ForkedRuler{ - LotexRuler: lotex, - GrafanaRuler: grafana, + LotexRuler: lotex, + GrafanaRuler: grafana, + DatasourceCache: datasourceCache, } } -func (r *ForkedRuler) backendType(ctx *models.ReqContext) apimodels.Backend { - // TODO: implement, hardcoded for now - return apimodels.LoTexRulerBackend +func (r *ForkedRuler) backendType(ctx *models.ReqContext) (apimodels.Backend, error) { + recipient := ctx.Params("Recipient") + if recipient == apimodels.GrafanaBackend.String() { + return apimodels.GrafanaBackend, nil + } + if datasourceID, err := strconv.ParseInt(recipient, 10, 64); err == nil { + if ds, err := r.DatasourceCache.GetDatasource(datasourceID, ctx.SignedInUser, ctx.SkipCache); err == nil { + switch ds.Type { + case "loki", "prometheus": + return apimodels.LoTexRulerBackend, nil + default: + return 0, fmt.Errorf("unexpected backend type (%v)", ds.Type) + } + } + } + return 0, fmt.Errorf("unexpected backend type (%v)", recipient) } func (r *ForkedRuler) RouteDeleteNamespaceRulesConfig(ctx *models.ReqContext) response.Response { - switch t := r.backendType(ctx); t { + t, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } + switch t { case apimodels.GrafanaBackend: return r.GrafanaRuler.RouteDeleteNamespaceRulesConfig(ctx) case apimodels.LoTexRulerBackend: @@ -37,7 +58,11 @@ func (r *ForkedRuler) RouteDeleteNamespaceRulesConfig(ctx *models.ReqContext) re } func (r *ForkedRuler) RouteDeleteRuleGroupConfig(ctx *models.ReqContext) response.Response { - switch t := r.backendType(ctx); t { + t, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } + switch t { case apimodels.GrafanaBackend: return r.GrafanaRuler.RouteDeleteRuleGroupConfig(ctx) case apimodels.LoTexRulerBackend: @@ -48,7 +73,11 @@ func (r *ForkedRuler) RouteDeleteRuleGroupConfig(ctx *models.ReqContext) respons } func (r *ForkedRuler) RouteGetNamespaceRulesConfig(ctx *models.ReqContext) response.Response { - switch t := r.backendType(ctx); t { + t, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } + switch t { case apimodels.GrafanaBackend: return r.GrafanaRuler.RouteGetNamespaceRulesConfig(ctx) case apimodels.LoTexRulerBackend: @@ -59,7 +88,11 @@ func (r *ForkedRuler) RouteGetNamespaceRulesConfig(ctx *models.ReqContext) respo } func (r *ForkedRuler) RouteGetRulegGroupConfig(ctx *models.ReqContext) response.Response { - switch t := r.backendType(ctx); t { + t, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } + switch t { case apimodels.GrafanaBackend: return r.GrafanaRuler.RouteGetRulegGroupConfig(ctx) case apimodels.LoTexRulerBackend: @@ -70,7 +103,11 @@ func (r *ForkedRuler) RouteGetRulegGroupConfig(ctx *models.ReqContext) response. } func (r *ForkedRuler) RouteGetRulesConfig(ctx *models.ReqContext) response.Response { - switch t := r.backendType(ctx); t { + t, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } + switch t { case apimodels.GrafanaBackend: return r.GrafanaRuler.RouteGetRulesConfig(ctx) case apimodels.LoTexRulerBackend: @@ -81,7 +118,10 @@ func (r *ForkedRuler) RouteGetRulesConfig(ctx *models.ReqContext) response.Respo } func (r *ForkedRuler) RoutePostNameRulesConfig(ctx *models.ReqContext, conf apimodels.RuleGroupConfig) response.Response { - backendType := r.backendType(ctx) + backendType, err := r.backendType(ctx) + if err != nil { + return response.Error(400, err.Error(), nil) + } payloadType := conf.Type() if backendType != payloadType { diff --git a/pkg/services/ngalert/api/lotex.go b/pkg/services/ngalert/api/lotex.go index ea30c82594f..b7d4cee84e0 100644 --- a/pkg/services/ngalert/api/lotex.go +++ b/pkg/services/ngalert/api/lotex.go @@ -57,7 +57,7 @@ func (r *LotexRuler) withReq( ) response.Response { newCtx, resp := replacedResponseWriter(ctx) newCtx.Req.Request = req - r.DataProxy.ProxyDatasourceRequestWithID(newCtx, ctx.ParamsInt64("DatasourceId")) + r.DataProxy.ProxyDatasourceRequestWithID(newCtx, ctx.ParamsInt64("Recipient")) status := resp.Status() if status >= 400 { diff --git a/pkg/services/ngalert/api/util_test.go b/pkg/services/ngalert/api/util_test.go index c5b27fd8cd2..a34f482fa84 100644 --- a/pkg/services/ngalert/api/util_test.go +++ b/pkg/services/ngalert/api/util_test.go @@ -16,8 +16,8 @@ func TestToMacaronPath(t *testing.T) { expectedOutputPath: "", }, { - inputPath: "/ruler/{DatasourceId}/api/v1/rules/{Namespace}/{Groupname}", - expectedOutputPath: "/ruler/:DatasourceId/api/v1/rules/:Namespace/:Groupname", + inputPath: "/ruler/{Recipient}/api/v1/rules/{Namespace}/{Groupname}", + expectedOutputPath: "/ruler/:Recipient/api/v1/rules/:Namespace/:Groupname", }, } for _, tc := range testCases {