2021-03-29 10:18:25 -05:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2021-04-07 14:36:50 -05:00
|
|
|
"bytes"
|
2021-03-29 10:18:25 -05:00
|
|
|
"encoding/json"
|
2021-10-01 08:24:56 -05:00
|
|
|
"errors"
|
2021-03-29 10:18:25 -05:00
|
|
|
"fmt"
|
2021-10-01 08:24:56 -05:00
|
|
|
"io"
|
2021-03-29 10:18:25 -05:00
|
|
|
"net/http"
|
|
|
|
|
2022-06-27 11:23:15 -05:00
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
|
2021-03-29 10:18:25 -05:00
|
|
|
"github.com/grafana/grafana/pkg/api/response"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
2023-01-27 01:50:36 -06:00
|
|
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
2022-06-27 11:23:15 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
2021-04-19 13:26:04 -05:00
|
|
|
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
2021-10-11 07:30:59 -05:00
|
|
|
"github.com/grafana/grafana/pkg/web"
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
|
2021-10-01 08:24:56 -05:00
|
|
|
var endpoints = map[string]map[string]string{
|
|
|
|
"cortex": {
|
|
|
|
"silences": "/alertmanager/api/v2/silences",
|
|
|
|
"silence": "/alertmanager/api/v2/silence/%s",
|
|
|
|
"status": "/alertmanager/api/v2/status",
|
|
|
|
"groups": "/alertmanager/api/v2/alerts/groups",
|
|
|
|
"alerts": "/alertmanager/api/v2/alerts",
|
|
|
|
"config": "/api/v1/alerts",
|
|
|
|
},
|
2022-06-20 05:56:38 -05:00
|
|
|
"mimir": {
|
|
|
|
"silences": "/alertmanager/api/v2/silences",
|
|
|
|
"silence": "/alertmanager/api/v2/silence/%s",
|
|
|
|
"status": "/alertmanager/api/v2/status",
|
|
|
|
"groups": "/alertmanager/api/v2/alerts/groups",
|
|
|
|
"alerts": "/alertmanager/api/v2/alerts",
|
|
|
|
"config": "/api/v1/alerts",
|
|
|
|
},
|
2021-10-01 08:24:56 -05:00
|
|
|
"prometheus": {
|
|
|
|
"silences": "/api/v2/silences",
|
|
|
|
"silence": "/api/v2/silence/%s",
|
|
|
|
"status": "/api/v2/status",
|
|
|
|
"groups": "/api/v2/alerts/groups",
|
|
|
|
"alerts": "/api/v2/alerts",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-03-29 10:18:25 -05:00
|
|
|
const (
|
2021-10-01 08:24:56 -05:00
|
|
|
defaultImplementation = "cortex"
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type LotexAM struct {
|
|
|
|
log log.Logger
|
|
|
|
*AlertingProxy
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLotexAM(proxy *AlertingProxy, log log.Logger) *LotexAM {
|
|
|
|
return &LotexAM{
|
|
|
|
log: log,
|
|
|
|
AlertingProxy: proxy,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 08:24:56 -05:00
|
|
|
func (am *LotexAM) withAMReq(
|
2023-01-27 01:50:36 -06:00
|
|
|
ctx *contextmodel.ReqContext,
|
2021-10-01 08:24:56 -05:00
|
|
|
method string,
|
|
|
|
endpoint string,
|
|
|
|
pathParams []string,
|
|
|
|
body io.Reader,
|
|
|
|
extractor func(*response.NormalResponse) (interface{}, error),
|
|
|
|
headers map[string]string,
|
|
|
|
) response.Response {
|
2022-04-29 02:25:22 -05:00
|
|
|
datasourceUID := web.Params(ctx.Req)[":DatasourceUID"]
|
|
|
|
if datasourceUID == "" {
|
|
|
|
return response.Error(http.StatusBadRequest, "DatasourceUID is invalid", nil)
|
2022-01-14 10:55:57 -06:00
|
|
|
}
|
|
|
|
|
2023-04-12 11:30:33 -05:00
|
|
|
ds, err := am.DataProxy.DataSourceCache.GetDatasourceByUID(ctx.Req.Context(), datasourceUID, ctx.SignedInUser, ctx.SkipDSCache)
|
2021-10-01 08:24:56 -05:00
|
|
|
if err != nil {
|
2022-06-27 11:23:15 -05:00
|
|
|
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
2021-10-01 08:24:56 -05:00
|
|
|
return ErrResp(http.StatusForbidden, err, "Access denied to datasource")
|
|
|
|
}
|
2022-06-27 11:23:15 -05:00
|
|
|
if errors.Is(err, datasources.ErrDataSourceNotFound) {
|
2021-10-01 08:24:56 -05:00
|
|
|
return ErrResp(http.StatusNotFound, err, "Unable to find datasource")
|
|
|
|
}
|
|
|
|
return ErrResp(http.StatusInternalServerError, err, "Unable to load datasource meta data")
|
|
|
|
}
|
|
|
|
|
|
|
|
impl := ds.JsonData.Get("implementation").MustString(defaultImplementation)
|
|
|
|
implEndpoints, ok := endpoints[impl]
|
|
|
|
if !ok {
|
|
|
|
return ErrResp(http.StatusBadRequest, fmt.Errorf("unsupported Alert Manager implementation \"%s\"", impl), "")
|
|
|
|
}
|
|
|
|
endpointPath, ok := implEndpoints[endpoint]
|
|
|
|
if !ok {
|
|
|
|
return ErrResp(http.StatusBadRequest, fmt.Errorf("unsupported endpoint \"%s\" for Alert Manager implementation \"%s\"", endpoint, impl), "")
|
|
|
|
}
|
|
|
|
|
|
|
|
iPathParams := make([]interface{}, len(pathParams))
|
|
|
|
for idx, value := range pathParams {
|
|
|
|
iPathParams[idx] = value
|
|
|
|
}
|
|
|
|
|
2021-06-15 11:14:02 -05:00
|
|
|
return am.withReq(
|
2021-10-01 08:24:56 -05:00
|
|
|
ctx,
|
|
|
|
method,
|
|
|
|
withPath(*ctx.Req.URL, fmt.Sprintf(endpointPath, iPathParams...)),
|
|
|
|
body,
|
|
|
|
extractor,
|
|
|
|
headers,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetAMStatus(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-06-15 11:14:02 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"status",
|
|
|
|
nil,
|
2021-06-15 11:14:02 -05:00
|
|
|
nil,
|
|
|
|
jsonExtractor(&apimodels.GettableStatus{}),
|
|
|
|
nil,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteCreateSilence(ctx *contextmodel.ReqContext, silenceBody apimodels.PostableSilence) response.Response {
|
2021-03-29 10:18:25 -05:00
|
|
|
blob, err := json.Marshal(silenceBody)
|
|
|
|
if err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(500, err, "Failed marshal silence")
|
2021-03-29 10:18:25 -05:00
|
|
|
}
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodPost,
|
2021-10-01 08:24:56 -05:00
|
|
|
"silences",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
bytes.NewBuffer(blob),
|
2022-10-17 14:43:37 -05:00
|
|
|
jsonExtractor(&apimodels.PostSilencesOKBody{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
map[string]string{"Content-Type": "application/json"},
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteDeleteAlertingConfig(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodDelete,
|
2021-10-01 08:24:56 -05:00
|
|
|
"config",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
messageExtractor,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteDeleteSilence(ctx *contextmodel.ReqContext, silenceID string) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodDelete,
|
2021-10-01 08:24:56 -05:00
|
|
|
"silence",
|
2022-06-23 15:13:39 -05:00
|
|
|
[]string{silenceID},
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
messageExtractor,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetAlertingConfig(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"config",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-04-07 00:42:43 -05:00
|
|
|
yamlExtractor(&apimodels.GettableUserConfig{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetAMAlertGroups(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"groups",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
jsonExtractor(&apimodels.AlertGroups{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetAMAlerts(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"alerts",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
jsonExtractor(&apimodels.GettableAlerts{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetSilence(ctx *contextmodel.ReqContext, silenceID string) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"silence",
|
2022-06-23 15:13:39 -05:00
|
|
|
[]string{silenceID},
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
jsonExtractor(&apimodels.GettableSilence{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RouteGetSilences(ctx *contextmodel.ReqContext) response.Response {
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodGet,
|
2021-10-01 08:24:56 -05:00
|
|
|
"silences",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
jsonExtractor(&apimodels.GettableSilences{}),
|
2021-04-07 14:36:50 -05:00
|
|
|
nil,
|
2021-03-29 10:18:25 -05:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RoutePostAlertingConfig(ctx *contextmodel.ReqContext, config apimodels.PostableUserConfig) response.Response {
|
2021-04-12 04:04:37 -05:00
|
|
|
yml, err := yaml.Marshal(&config)
|
2021-03-29 10:18:25 -05:00
|
|
|
if err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(500, err, "Failed marshal alert manager configuration ")
|
2021-03-29 10:18:25 -05:00
|
|
|
}
|
2021-04-07 14:36:50 -05:00
|
|
|
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodPost,
|
2021-10-01 08:24:56 -05:00
|
|
|
"config",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
bytes.NewBuffer(yml),
|
|
|
|
messageExtractor,
|
|
|
|
nil,
|
|
|
|
)
|
2021-03-29 10:18:25 -05:00
|
|
|
}
|
|
|
|
|
2023-01-27 01:50:36 -06:00
|
|
|
func (am *LotexAM) RoutePostAMAlerts(ctx *contextmodel.ReqContext, alerts apimodels.PostableAlerts) response.Response {
|
2021-03-29 10:18:25 -05:00
|
|
|
yml, err := yaml.Marshal(alerts)
|
|
|
|
if err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(500, err, "Failed marshal postable alerts")
|
2021-03-29 10:18:25 -05:00
|
|
|
}
|
2021-04-07 14:36:50 -05:00
|
|
|
|
2021-10-01 08:24:56 -05:00
|
|
|
return am.withAMReq(
|
2021-04-07 14:36:50 -05:00
|
|
|
ctx,
|
|
|
|
http.MethodPost,
|
2021-10-01 08:24:56 -05:00
|
|
|
"alerts",
|
|
|
|
nil,
|
2021-04-07 14:36:50 -05:00
|
|
|
bytes.NewBuffer(yml),
|
|
|
|
messageExtractor,
|
|
|
|
nil,
|
|
|
|
)
|
2021-03-29 10:18:25 -05:00
|
|
|
}
|