mirror of https://github.com/grafana/grafana
[Alerting] Forking alert manager API (#32300)
* Alertmanager lotex ruler * Apply suggestions from code reviewpull/32443/head
parent
740c5813d4
commit
c4d5a67b38
@ -0,0 +1,129 @@ |
|||||||
|
package api |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
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" |
||||||
|
) |
||||||
|
|
||||||
|
type ForkedAMSvc struct { |
||||||
|
AMSvc, GrafanaSvc AlertmanagerApiService |
||||||
|
DatasourceCache datasources.CacheService |
||||||
|
} |
||||||
|
|
||||||
|
func NewForkedAM(datasourceCache datasources.CacheService, proxy, grafana AlertmanagerApiService) *ForkedAMSvc { |
||||||
|
return &ForkedAMSvc{ |
||||||
|
AMSvc: proxy, |
||||||
|
GrafanaSvc: grafana, |
||||||
|
DatasourceCache: datasourceCache, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) getService(ctx *models.ReqContext) (AlertmanagerApiService, error) { |
||||||
|
t, err := backendType(ctx, am.DatasourceCache) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
switch t { |
||||||
|
case apimodels.GrafanaBackend: |
||||||
|
return am.GrafanaSvc, nil |
||||||
|
case apimodels.AlertmanagerBackend: |
||||||
|
return am.AMSvc, nil |
||||||
|
default: |
||||||
|
return nil, fmt.Errorf("unexpected backend type (%v)", t) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteCreateSilence(ctx *models.ReqContext, body apimodels.SilenceBody) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteCreateSilence(ctx, body) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteDeleteAlertingConfig(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteDeleteAlertingConfig(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteDeleteSilence(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteDeleteSilence(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteGetAlertingConfig(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteGetAlertingConfig(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteGetAMAlertGroups(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteGetAMAlertGroups(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteGetAMAlerts(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteGetAMAlerts(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteGetSilence(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteGetSilence(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RouteGetSilences(ctx *models.ReqContext) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RouteGetSilences(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RoutePostAlertingConfig(ctx *models.ReqContext, body apimodels.PostableUserConfig) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RoutePostAlertingConfig(ctx, body) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *ForkedAMSvc) RoutePostAMAlerts(ctx *models.ReqContext, body apimodels.PostableAlerts) response.Response { |
||||||
|
s, err := am.getService(ctx) |
||||||
|
if err != nil { |
||||||
|
return response.Error(400, err.Error(), nil) |
||||||
|
} |
||||||
|
|
||||||
|
return s.RoutePostAMAlerts(ctx, body) |
||||||
|
} |
@ -0,0 +1,170 @@ |
|||||||
|
package api |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"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" |
||||||
|
"gopkg.in/yaml.v3" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
amSilencesPath = "/api/v2/silences" |
||||||
|
amSilencePath = "/api/v2/silence/%s" |
||||||
|
amAlertGroupsPath = "/api/v2/alerts/groups" |
||||||
|
amAlertsPath = "/api/v2/alerts" |
||||||
|
amConfigPath = "/api/v1/alerts" |
||||||
|
) |
||||||
|
|
||||||
|
type LotexAM struct { |
||||||
|
log log.Logger |
||||||
|
*AlertingProxy |
||||||
|
} |
||||||
|
|
||||||
|
func NewLotexAM(proxy *AlertingProxy, log log.Logger) *LotexAM { |
||||||
|
return &LotexAM{ |
||||||
|
log: log, |
||||||
|
AlertingProxy: proxy, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteCreateSilence(ctx *models.ReqContext, silenceBody apimodels.SilenceBody) response.Response { |
||||||
|
blob, err := json.Marshal(silenceBody) |
||||||
|
if err != nil { |
||||||
|
return response.Error(500, "Failed marshal silence", err) |
||||||
|
} |
||||||
|
body, ln := payload(blob) |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
Method: "POST", |
||||||
|
URL: withPath(*ctx.Req.URL, amSilencesPath), |
||||||
|
Body: body, |
||||||
|
ContentLength: ln, |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.GettableSilence{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteDeleteAlertingConfig(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
Method: "DELETE", |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
amConfigPath, |
||||||
|
), |
||||||
|
}, |
||||||
|
messageExtractor, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteDeleteSilence(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
Method: "DELETE", |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")), |
||||||
|
), |
||||||
|
}, |
||||||
|
messageExtractor, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteGetAlertingConfig(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
amConfigPath, |
||||||
|
), |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.GettableUserConfig{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteGetAMAlertGroups(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
amAlertGroupsPath, |
||||||
|
), |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.AlertGroups{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteGetAMAlerts(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
amAlertsPath, |
||||||
|
), |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.GettableAlerts{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteGetSilence(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")), |
||||||
|
), |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.GettableSilence{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RouteGetSilences(ctx *models.ReqContext) response.Response { |
||||||
|
return am.withReq( |
||||||
|
ctx, &http.Request{ |
||||||
|
URL: withPath( |
||||||
|
*ctx.Req.URL, |
||||||
|
amSilencesPath, |
||||||
|
), |
||||||
|
}, |
||||||
|
jsonExtractor(&apimodels.GettableSilences{}), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RoutePostAlertingConfig(ctx *models.ReqContext, config apimodels.PostableUserConfig) response.Response { |
||||||
|
yml, err := yaml.Marshal(config) |
||||||
|
if err != nil { |
||||||
|
return response.Error(500, "Failed marshal alert manager configuration ", err) |
||||||
|
} |
||||||
|
body, ln := payload(yml) |
||||||
|
|
||||||
|
u := withPath(*ctx.Req.URL, amConfigPath) |
||||||
|
req := &http.Request{ |
||||||
|
Method: "POST", |
||||||
|
URL: u, |
||||||
|
Body: body, |
||||||
|
ContentLength: ln, |
||||||
|
} |
||||||
|
return am.withReq(ctx, req, messageExtractor) |
||||||
|
} |
||||||
|
|
||||||
|
func (am *LotexAM) RoutePostAMAlerts(ctx *models.ReqContext, alerts apimodels.PostableAlerts) response.Response { |
||||||
|
yml, err := yaml.Marshal(alerts) |
||||||
|
if err != nil { |
||||||
|
return response.Error(500, "Failed marshal postable alerts", err) |
||||||
|
} |
||||||
|
body, ln := payload(yml) |
||||||
|
|
||||||
|
u := withPath(*ctx.Req.URL, amAlertsPath) |
||||||
|
req := &http.Request{ |
||||||
|
Method: "POST", |
||||||
|
URL: u, |
||||||
|
Body: body, |
||||||
|
ContentLength: ln, |
||||||
|
} |
||||||
|
return am.withReq(ctx, req, messageExtractor) |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
@lokiDatasourceID = 32 |
||||||
|
@prometheusDatasourceID = 875 |
||||||
|
@grafana = grafana |
||||||
|
|
||||||
|
# unsupported loki backend |
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{lokiDatasourceID}}/config/api/v1/alerts |
||||||
|
|
||||||
|
### |
||||||
|
# unsupported cortex backend |
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{prometheusDatasourceID}}/config/api/v1/alerts |
||||||
|
|
||||||
|
### |
||||||
|
# grafana requests |
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts |
||||||
|
|
||||||
|
### |
||||||
|
DELETE http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts |
||||||
|
|
||||||
|
### |
||||||
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/config/api/v1/alerts |
||||||
|
content-type: application/json |
||||||
|
|
||||||
|
< ./post-user-config.json |
||||||
|
|
||||||
|
### |
||||||
|
POST http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences |
||||||
|
content-type: application/json |
||||||
|
|
||||||
|
< ./post-silence-data.json |
||||||
|
|
||||||
|
### |
||||||
|
GET http://admin:admin@localhost:3000/alertmanager/{{grafana}}/api/v2/silences |
Loading…
Reference in new issue