2021-04-13 12:58:34 -05:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2021-12-22 04:02:42 -06:00
|
|
|
"context"
|
2021-05-28 10:55:03 -05:00
|
|
|
"errors"
|
2021-04-13 12:58:34 -05:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strconv"
|
|
|
|
|
2022-04-01 19:00:23 -05:00
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
|
|
|
2021-04-13 12:58:34 -05:00
|
|
|
"github.com/grafana/grafana/pkg/api/response"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-04-01 19:00:23 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
2021-04-13 12:58:34 -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-04-21 14:44:50 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
2022-04-01 19:00:23 -05:00
|
|
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2021-04-13 12:58:34 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type TestingApiSrv struct {
|
|
|
|
*AlertingProxy
|
2022-06-27 16:40:44 -05:00
|
|
|
DatasourceCache datasources.CacheService
|
|
|
|
log log.Logger
|
|
|
|
accessControl accesscontrol.AccessControl
|
|
|
|
evaluator eval.Evaluator
|
2021-04-13 12:58:34 -05:00
|
|
|
}
|
|
|
|
|
2022-02-04 11:42:04 -06:00
|
|
|
func (srv TestingApiSrv) RouteTestGrafanaRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload) response.Response {
|
|
|
|
if body.Type() != apimodels.GrafanaBackend || body.GrafanaManagedCondition == nil {
|
|
|
|
return ErrResp(http.StatusBadRequest, errors.New("unexpected payload"), "")
|
2021-04-13 12:58:34 -05:00
|
|
|
}
|
2022-04-01 19:00:23 -05:00
|
|
|
|
|
|
|
if !authorizeDatasourceAccessForRule(&ngmodels.AlertRule{Data: body.GrafanaManagedCondition.Data}, func(evaluator accesscontrol.Evaluator) bool {
|
|
|
|
return accesscontrol.HasAccess(srv.accessControl, c)(accesscontrol.ReqSignedIn, evaluator)
|
|
|
|
}) {
|
|
|
|
return ErrResp(http.StatusUnauthorized, fmt.Errorf("%w to query one or many data sources used by the rule", ErrAuthorization), "")
|
|
|
|
}
|
|
|
|
|
|
|
|
evalCond := ngmodels.Condition{
|
|
|
|
Condition: body.GrafanaManagedCondition.Condition,
|
|
|
|
OrgID: c.SignedInUser.OrgId,
|
|
|
|
Data: body.GrafanaManagedCondition.Data,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := validateCondition(c.Req.Context(), evalCond, c.SignedInUser, c.SkipCache, srv.DatasourceCache); err != nil {
|
|
|
|
return ErrResp(http.StatusBadRequest, err, "invalid condition")
|
|
|
|
}
|
|
|
|
|
|
|
|
now := body.GrafanaManagedCondition.Now
|
|
|
|
if now.IsZero() {
|
|
|
|
now = timeNow()
|
|
|
|
}
|
|
|
|
|
2022-07-12 15:51:32 -05:00
|
|
|
evalResults := srv.evaluator.ConditionEval(evalCond, now)
|
2022-04-01 19:00:23 -05:00
|
|
|
|
|
|
|
frame := evalResults.AsDataFrame()
|
|
|
|
return response.JSONStreaming(http.StatusOK, util.DynMap{
|
|
|
|
"instances": []*data.Frame{&frame},
|
|
|
|
})
|
2022-02-04 11:42:04 -06:00
|
|
|
}
|
2021-04-13 12:58:34 -05:00
|
|
|
|
2022-06-23 15:13:39 -05:00
|
|
|
func (srv TestingApiSrv) RouteTestRuleConfig(c *models.ReqContext, body apimodels.TestRulePayload, datasourceUID string) response.Response {
|
2021-04-13 12:58:34 -05:00
|
|
|
if body.Type() != apimodels.LoTexRulerBackend {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(http.StatusBadRequest, errors.New("unexpected payload"), "")
|
2021-04-13 12:58:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var path string
|
2022-05-17 06:10:20 -05:00
|
|
|
ds, err := srv.DatasourceCache.GetDatasourceByUID(context.Background(), datasourceUID, c.SignedInUser, c.SkipCache)
|
|
|
|
if err != nil {
|
|
|
|
return ErrResp(http.StatusInternalServerError, err, "failed to get datasource")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ds.Type {
|
|
|
|
case "loki":
|
|
|
|
path = "loki/api/v1/query"
|
|
|
|
case "prometheus":
|
|
|
|
path = "api/v1/query"
|
|
|
|
default:
|
|
|
|
return ErrResp(http.StatusBadRequest, fmt.Errorf("unexpected datasource type %s", ds.Type), "")
|
2021-04-13 12:58:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
t := timeNow()
|
|
|
|
queryURL, err := url.Parse(path)
|
|
|
|
if err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(http.StatusInternalServerError, err, "failed to parse url")
|
2021-04-13 12:58:34 -05:00
|
|
|
}
|
|
|
|
params := queryURL.Query()
|
|
|
|
params.Set("query", body.Expr)
|
|
|
|
params.Set("time", strconv.FormatInt(t.Unix(), 10))
|
|
|
|
queryURL.RawQuery = params.Encode()
|
|
|
|
return srv.withReq(
|
|
|
|
c,
|
|
|
|
http.MethodGet,
|
|
|
|
queryURL,
|
|
|
|
nil,
|
2021-05-25 10:54:50 -05:00
|
|
|
instantQueryResultsExtractor,
|
2021-04-13 12:58:34 -05:00
|
|
|
nil,
|
|
|
|
)
|
|
|
|
}
|
2021-04-21 14:44:50 -05:00
|
|
|
|
|
|
|
func (srv TestingApiSrv) RouteEvalQueries(c *models.ReqContext, cmd apimodels.EvalQueriesPayload) response.Response {
|
|
|
|
now := cmd.Now
|
|
|
|
if now.IsZero() {
|
|
|
|
now = timeNow()
|
|
|
|
}
|
2021-04-28 03:31:51 -05:00
|
|
|
|
2022-04-01 19:00:23 -05:00
|
|
|
if !authorizeDatasourceAccessForRule(&ngmodels.AlertRule{Data: cmd.Data}, func(evaluator accesscontrol.Evaluator) bool {
|
|
|
|
return accesscontrol.HasAccess(srv.accessControl, c)(accesscontrol.ReqSignedIn, evaluator)
|
|
|
|
}) {
|
|
|
|
return ErrResp(http.StatusUnauthorized, fmt.Errorf("%w to query one or many data sources used by the rule", ErrAuthorization), "")
|
|
|
|
}
|
|
|
|
|
2021-12-20 10:05:33 -06:00
|
|
|
if _, err := validateQueriesAndExpressions(c.Req.Context(), cmd.Data, c.SignedInUser, c.SkipCache, srv.DatasourceCache); err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(http.StatusBadRequest, err, "invalid queries or expressions")
|
2021-04-21 14:44:50 -05:00
|
|
|
}
|
|
|
|
|
2022-06-27 16:40:44 -05:00
|
|
|
evalResults, err := srv.evaluator.QueriesAndExpressionsEval(c.SignedInUser.OrgId, cmd.Data, now)
|
2021-04-21 14:44:50 -05:00
|
|
|
if err != nil {
|
2021-05-28 10:55:03 -05:00
|
|
|
return ErrResp(http.StatusBadRequest, err, "Failed to evaluate queries and expressions")
|
2021-04-21 14:44:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return response.JSONStreaming(http.StatusOK, evalResults)
|
|
|
|
}
|