From 0779dab0de2ed4ea1cdf2f28992cfef34dcc81d8 Mon Sep 17 00:00:00 2001 From: Domas Date: Mon, 29 Mar 2021 08:55:09 +0300 Subject: [PATCH] NgAlerting: loki & cortex have different prom & ruler endpoint prefixes (#32344) --- pkg/services/ngalert/api/lotex.go | 43 ++++++++++++++++++++++-- pkg/services/ngalert/api/lotex_prom.go | 45 ++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/pkg/services/ngalert/api/lotex.go b/pkg/services/ngalert/api/lotex.go index 8f1968de3e7..b7ac8fc20d1 100644 --- a/pkg/services/ngalert/api/lotex.go +++ b/pkg/services/ngalert/api/lotex.go @@ -16,7 +16,10 @@ import ( "github.com/grafana/grafana/pkg/models" ) -const legacyRulerPrefix = "/api/prom/rules" +var dsTypeToRulerPrefix = map[string]string{ + "prometheus": "/rules", + "loki": "/api/prom/rules", +} type LotexRuler struct { log log.Logger @@ -31,13 +34,17 @@ func NewLotexRuler(proxy *AlertingProxy, log log.Logger) *LotexRuler { } func (r *LotexRuler) RouteDeleteNamespaceRulesConfig(ctx *models.ReqContext) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } return r.withReq( ctx, &http.Request{ Method: "DELETE", URL: withPath( *ctx.Req.URL, - fmt.Sprintf("/api/prom/rules/%s", ctx.Params("Namespace")), + fmt.Sprintf("%s/%s", legacyRulerPrefix, ctx.Params("Namespace")), ), }, messageExtractor, @@ -45,6 +52,10 @@ func (r *LotexRuler) RouteDeleteNamespaceRulesConfig(ctx *models.ReqContext) res } func (r *LotexRuler) RouteDeleteRuleGroupConfig(ctx *models.ReqContext) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } return r.withReq( ctx, &http.Request{ @@ -64,6 +75,10 @@ func (r *LotexRuler) RouteDeleteRuleGroupConfig(ctx *models.ReqContext) response } func (r *LotexRuler) RouteGetNamespaceRulesConfig(ctx *models.ReqContext) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } return r.withReq( ctx, &http.Request{ URL: withPath( @@ -80,6 +95,10 @@ func (r *LotexRuler) RouteGetNamespaceRulesConfig(ctx *models.ReqContext) respon } func (r *LotexRuler) RouteGetRulegGroupConfig(ctx *models.ReqContext) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } return r.withReq( ctx, &http.Request{ @@ -98,6 +117,10 @@ func (r *LotexRuler) RouteGetRulegGroupConfig(ctx *models.ReqContext) response.R } func (r *LotexRuler) RouteGetRulesConfig(ctx *models.ReqContext) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } return r.withReq( ctx, &http.Request{ @@ -111,6 +134,10 @@ func (r *LotexRuler) RouteGetRulesConfig(ctx *models.ReqContext) response.Respon } func (r *LotexRuler) RoutePostNameRulesConfig(ctx *models.ReqContext, conf apimodels.RuleGroupConfig) response.Response { + legacyRulerPrefix, err := r.getPrefix(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } yml, err := yaml.Marshal(conf) if err != nil { return response.Error(500, "Failed marshal rule group", err) @@ -129,6 +156,18 @@ func (r *LotexRuler) RoutePostNameRulesConfig(ctx *models.ReqContext, conf apimo return r.withReq(ctx, req, jsonExtractor(nil)) } +func (r *LotexRuler) getPrefix(ctx *models.ReqContext) (string, error) { + ds, err := r.DataProxy.DatasourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache) + if err != nil { + return "", err + } + prefix, ok := dsTypeToRulerPrefix[ds.Type] + if !ok { + return "", fmt.Errorf("unexpected datasource type. expecting loki or prometheus") + } + return prefix, nil +} + func withPath(u url.URL, newPath string) *url.URL { // TODO: handle path escaping u.Path = newPath diff --git a/pkg/services/ngalert/api/lotex_prom.go b/pkg/services/ngalert/api/lotex_prom.go index 825ca91a870..01bc4e7fd0b 100644 --- a/pkg/services/ngalert/api/lotex_prom.go +++ b/pkg/services/ngalert/api/lotex_prom.go @@ -1,6 +1,7 @@ package api import ( + "fmt" "net/http" apimodels "github.com/grafana/alerting-api/pkg/api" @@ -9,10 +10,20 @@ import ( "github.com/grafana/grafana/pkg/models" ) -const ( - promRulesPath = "/prometheus/api/v1/rules" - promAlertsPath = "/prometheus/api/v1/alerts" -) +type promEndpoints struct { + rules, alerts string +} + +var dsTypeToLotexRoutes = map[string]promEndpoints{ + "prometheus": { + rules: "/api/v1/rules", + alerts: "/api/v1/alerts", + }, + "loki": { + rules: "/prometheus/api/v1/rules", + alerts: "/prometheus/api/v1/alerts", + }, +} type LotexProm struct { log log.Logger @@ -27,11 +38,16 @@ func NewLotexProm(proxy *AlertingProxy, log log.Logger) *LotexProm { } func (p *LotexProm) RouteGetAlertStatuses(ctx *models.ReqContext) response.Response { + endpoints, err := p.getEndpoints(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } + return p.withReq( ctx, &http.Request{ URL: withPath( *ctx.Req.URL, - promAlertsPath, + endpoints.alerts, ), }, jsonExtractor(&apimodels.AlertResponse{}), @@ -39,13 +55,30 @@ func (p *LotexProm) RouteGetAlertStatuses(ctx *models.ReqContext) response.Respo } func (p *LotexProm) RouteGetRuleStatuses(ctx *models.ReqContext) response.Response { + endpoints, err := p.getEndpoints(ctx) + if err != nil { + return response.Error(500, err.Error(), nil) + } + return p.withReq( ctx, &http.Request{ URL: withPath( *ctx.Req.URL, - promRulesPath, + endpoints.rules, ), }, jsonExtractor(&apimodels.RuleResponse{}), ) } + +func (p *LotexProm) getEndpoints(ctx *models.ReqContext) (*promEndpoints, error) { + ds, err := p.DataProxy.DatasourceCache.GetDatasource(ctx.ParamsInt64("Recipient"), ctx.SignedInUser, ctx.SkipCache) + if err != nil { + return nil, err + } + routes, ok := dsTypeToLotexRoutes[ds.Type] + if !ok { + return nil, fmt.Errorf("unexpected datasource type. expecting loki or prometheus") + } + return &routes, nil +}