[Alerting]: implement backend checking for forking to Lotex ruler (#32208)

* Rename DatasourceId path parameter

* Implement fork ruler backendType()

* Apply suggestions from code review
pull/32265/head
Sofia Papagiannaki 4 years ago committed by GitHub
parent 94ac097f47
commit 24cb059a6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      go.mod
  2. 5
      go.sum
  3. 1
      pkg/services/ngalert/api/api.go
  4. 60
      pkg/services/ngalert/api/api_alertmanager_base.go
  5. 42
      pkg/services/ngalert/api/api_alertmanager_mock.go
  6. 12
      pkg/services/ngalert/api/api_prometheus_base.go
  7. 8
      pkg/services/ngalert/api/api_prometheus_mock.go
  8. 36
      pkg/services/ngalert/api/api_ruler_base.go
  9. 24
      pkg/services/ngalert/api/api_ruler_mock.go
  10. 64
      pkg/services/ngalert/api/fork_ruler.go
  11. 2
      pkg/services/ngalert/api/lotex.go
  12. 4
      pkg/services/ngalert/api/util_test.go

@ -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

@ -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=

@ -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},
))

@ -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)
}

@ -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"})
}

@ -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)
}

@ -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{

@ -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)

@ -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)

@ -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 {

@ -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 {

@ -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 {

Loading…
Cancel
Save