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