Commit 6415d280 by Joan López de la Franca Beltran Committed by GitHub

Plugins: Requests validator (#30445)

* Introduce PluginRequestValidator abstraction with a NoOp implementation

* Update PluginRequestValidator abstraction to use the dsURL instead

* Inject PluginRequestValidator into the HTTPServer and validate requests going through data source proxy

* Inject PluginRequestValidator into the BackendPluginManager and validate requests going through it

* Validate requests going through QueryMetrics & QueryMetricsV2

* Validate BackendPluginManager health requests

* Fix backend plugins manager tests

* Validate requests going through alerting service

* Fix tests

* fix tests

* goimports

Co-authored-by: Leonard Gram <leo@xlson.com>
parent 4a324e3d
...@@ -3,6 +3,7 @@ package api ...@@ -3,6 +3,7 @@ package api
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/grafana/grafana/pkg/api/datasource" "github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/api/pluginproxy" "github.com/grafana/grafana/pkg/api/pluginproxy"
...@@ -19,17 +20,23 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) { ...@@ -19,17 +20,23 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
ds, err := hs.DatasourceCache.GetDatasource(dsID, c.SignedInUser, c.SkipCache) ds, err := hs.DatasourceCache.GetDatasource(dsID, c.SignedInUser, c.SkipCache)
if err != nil { if err != nil {
if errors.Is(err, models.ErrDataSourceAccessDenied) { if errors.Is(err, models.ErrDataSourceAccessDenied) {
c.JsonApiErr(403, "Access denied to datasource", err) c.JsonApiErr(http.StatusForbidden, "Access denied to datasource", err)
return return
} }
c.JsonApiErr(500, "Unable to load datasource meta data", err) c.JsonApiErr(http.StatusInternalServerError, "Unable to load datasource meta data", err)
return
}
err = hs.PluginRequestValidator.Validate(ds.Url, c.Req.Request)
if err != nil {
c.JsonApiErr(http.StatusForbidden, "Access denied", err)
return return
} }
// find plugin // find plugin
plugin, ok := plugins.DataSources[ds.Type] plugin, ok := plugins.DataSources[ds.Type]
if !ok { if !ok {
c.JsonApiErr(500, "Unable to find datasource plugin", err) c.JsonApiErr(http.StatusInternalServerError, "Unable to find datasource plugin", err)
return return
} }
...@@ -39,9 +46,9 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) { ...@@ -39,9 +46,9 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin, c, proxyPath, hs.Cfg) proxy, err := pluginproxy.NewDataSourceProxy(ds, plugin, c, proxyPath, hs.Cfg)
if err != nil { if err != nil {
if errors.Is(err, datasource.URLValidationError{}) { if errors.Is(err, datasource.URLValidationError{}) {
c.JsonApiErr(400, fmt.Sprintf("Invalid data source URL: %q", ds.Url), err) c.JsonApiErr(http.StatusBadRequest, fmt.Sprintf("Invalid data source URL: %q", ds.Url), err)
} else { } else {
c.JsonApiErr(500, "Failed creating data source proxy", err) c.JsonApiErr(http.StatusInternalServerError, "Failed creating data source proxy", err)
} }
return return
} }
......
...@@ -60,28 +60,29 @@ type HTTPServer struct { ...@@ -60,28 +60,29 @@ type HTTPServer struct {
httpSrv *http.Server httpSrv *http.Server
middlewares []macaron.Handler middlewares []macaron.Handler
RouteRegister routing.RouteRegister `inject:""` RouteRegister routing.RouteRegister `inject:""`
Bus bus.Bus `inject:""` Bus bus.Bus `inject:""`
RenderService rendering.Service `inject:""` RenderService rendering.Service `inject:""`
Cfg *setting.Cfg `inject:""` Cfg *setting.Cfg `inject:""`
HooksService *hooks.HooksService `inject:""` HooksService *hooks.HooksService `inject:""`
CacheService *localcache.CacheService `inject:""` CacheService *localcache.CacheService `inject:""`
DatasourceCache datasources.CacheService `inject:""` DatasourceCache datasources.CacheService `inject:""`
AuthTokenService models.UserTokenService `inject:""` AuthTokenService models.UserTokenService `inject:""`
QuotaService *quota.QuotaService `inject:""` QuotaService *quota.QuotaService `inject:""`
RemoteCacheService *remotecache.RemoteCache `inject:""` RemoteCacheService *remotecache.RemoteCache `inject:""`
ProvisioningService provisioning.ProvisioningService `inject:""` ProvisioningService provisioning.ProvisioningService `inject:""`
Login *login.LoginService `inject:""` Login *login.LoginService `inject:""`
License models.Licensing `inject:""` License models.Licensing `inject:""`
BackendPluginManager backendplugin.Manager `inject:""` BackendPluginManager backendplugin.Manager `inject:""`
PluginManager *plugins.PluginManager `inject:""` PluginRequestValidator models.PluginRequestValidator `inject:""`
SearchService *search.SearchService `inject:""` PluginManager *plugins.PluginManager `inject:""`
ShortURLService *shorturls.ShortURLService `inject:""` SearchService *search.SearchService `inject:""`
Live *live.GrafanaLive `inject:""` ShortURLService *shorturls.ShortURLService `inject:""`
ContextHandler *contexthandler.ContextHandler `inject:""` Live *live.GrafanaLive `inject:""`
SQLStore *sqlstore.SQLStore `inject:""` ContextHandler *contexthandler.ContextHandler `inject:""`
LibraryPanelService *librarypanels.LibraryPanelService `inject:""` SQLStore *sqlstore.SQLStore `inject:""`
Listener net.Listener LibraryPanelService *librarypanels.LibraryPanelService `inject:""`
Listener net.Listener
} }
func (hs *HTTPServer) Init() error { func (hs *HTTPServer) Init() error {
......
...@@ -3,6 +3,7 @@ package api ...@@ -3,6 +3,7 @@ package api
import ( import (
"context" "context"
"errors" "errors"
"net/http"
"github.com/grafana/grafana/pkg/expr" "github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
...@@ -19,7 +20,7 @@ import ( ...@@ -19,7 +20,7 @@ import (
// POST /api/ds/query DataSource query w/ expressions // POST /api/ds/query DataSource query w/ expressions
func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricRequest) response.Response { func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricRequest) response.Response {
if len(reqDTO.Queries) == 0 { if len(reqDTO.Queries) == 0 {
return response.Error(400, "No queries found in query", nil) return response.Error(http.StatusBadRequest, "No queries found in query", nil)
} }
request := &tsdb.TsdbQuery{ request := &tsdb.TsdbQuery{
...@@ -43,7 +44,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq ...@@ -43,7 +44,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
datasourceID, err := query.Get("datasourceId").Int64() datasourceID, err := query.Get("datasourceId").Int64()
if err != nil { if err != nil {
hs.log.Debug("Can't process query since it's missing data source ID") hs.log.Debug("Can't process query since it's missing data source ID")
return response.Error(400, "Query missing data source ID", nil) return response.Error(http.StatusBadRequest, "Query missing data source ID", nil)
} }
// For mixed datasource case, each data source is sent in a single request. // For mixed datasource case, each data source is sent in a single request.
...@@ -66,17 +67,22 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq ...@@ -66,17 +67,22 @@ func (hs *HTTPServer) QueryMetricsV2(c *models.ReqContext, reqDTO dtos.MetricReq
}) })
} }
err := hs.PluginRequestValidator.Validate(ds.Url, nil)
if err != nil {
return response.Error(http.StatusForbidden, "Access denied", err)
}
resp, err := tsdb.HandleRequest(c.Req.Context(), ds, request) resp, err := tsdb.HandleRequest(c.Req.Context(), ds, request)
if err != nil { if err != nil {
return response.Error(500, "Metric request error", err) return response.Error(http.StatusInternalServerError, "Metric request error", err)
} }
statusCode := 200 statusCode := http.StatusOK
for _, res := range resp.Results { for _, res := range resp.Results {
if res.Error != nil { if res.Error != nil {
res.ErrorString = res.Error.Error() res.ErrorString = res.Error.Error()
resp.Message = res.ErrorString resp.Message = res.ErrorString
statusCode = 400 statusCode = http.StatusBadRequest
} }
} }
...@@ -154,12 +160,12 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque ...@@ -154,12 +160,12 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To) timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To)
if len(reqDto.Queries) == 0 { if len(reqDto.Queries) == 0 {
return response.Error(400, "No queries found in query", nil) return response.Error(http.StatusBadRequest, "No queries found in query", nil)
} }
datasourceId, err := reqDto.Queries[0].Get("datasourceId").Int64() datasourceId, err := reqDto.Queries[0].Get("datasourceId").Int64()
if err != nil { if err != nil {
return response.Error(400, "Query missing datasourceId", nil) return response.Error(http.StatusBadRequest, "Query missing datasourceId", nil)
} }
ds, err := hs.DatasourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache) ds, err := hs.DatasourceCache.GetDatasource(datasourceId, c.SignedInUser, c.SkipCache)
...@@ -167,6 +173,11 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque ...@@ -167,6 +173,11 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
return hs.handleGetDataSourceError(err, datasourceId) return hs.handleGetDataSourceError(err, datasourceId)
} }
err = hs.PluginRequestValidator.Validate(ds.Url, nil)
if err != nil {
return response.Error(http.StatusForbidden, "Access denied", err)
}
request := &tsdb.TsdbQuery{ request := &tsdb.TsdbQuery{
TimeRange: timeRange, TimeRange: timeRange,
Debug: reqDto.Debug, Debug: reqDto.Debug,
...@@ -185,15 +196,15 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque ...@@ -185,15 +196,15 @@ func (hs *HTTPServer) QueryMetrics(c *models.ReqContext, reqDto dtos.MetricReque
resp, err := tsdb.HandleRequest(c.Req.Context(), ds, request) resp, err := tsdb.HandleRequest(c.Req.Context(), ds, request)
if err != nil { if err != nil {
return response.Error(500, "Metric request error", err) return response.Error(http.StatusInternalServerError, "Metric request error", err)
} }
statusCode := 200 statusCode := http.StatusOK
for _, res := range resp.Results { for _, res := range resp.Results {
if res.Error != nil { if res.Error != nil {
res.ErrorString = res.Error.Error() res.ErrorString = res.Error.Error()
resp.Message = res.ErrorString resp.Message = res.ErrorString
statusCode = 400 statusCode = http.StatusBadRequest
} }
} }
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
_ "github.com/gobwas/glob" _ "github.com/gobwas/glob"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/licensing" "github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/validations"
_ "github.com/grafana/loki/pkg/logproto" _ "github.com/grafana/loki/pkg/logproto"
_ "github.com/grpc-ecosystem/go-grpc-middleware" _ "github.com/grpc-ecosystem/go-grpc-middleware"
_ "github.com/jung-kurt/gofpdf" _ "github.com/jung-kurt/gofpdf"
...@@ -26,6 +27,7 @@ import ( ...@@ -26,6 +27,7 @@ import (
func init() { func init() {
registry.RegisterService(&licensing.OSSLicensingService{}) registry.RegisterService(&licensing.OSSLicensingService{})
registry.RegisterService(&validations.OSSPluginRequestValidator{})
} }
var IsEnterprise bool = false var IsEnterprise bool = false
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
package mock_gcsifaces package mock_gcsifaces
import ( import (
storage "cloud.google.com/go/storage"
context "context" context "context"
reflect "reflect"
storage "cloud.google.com/go/storage"
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
gcsifaces "github.com/grafana/grafana/pkg/ifaces/gcsifaces" gcsifaces "github.com/grafana/grafana/pkg/ifaces/gcsifaces"
google "golang.org/x/oauth2/google" google "golang.org/x/oauth2/google"
jwt "golang.org/x/oauth2/jwt" jwt "golang.org/x/oauth2/jwt"
reflect "reflect"
) )
// MockStorageClient is a mock of StorageClient interface // MockStorageClient is a mock of StorageClient interface
......
package models
import (
"net/http"
)
type PluginRequestValidator interface {
// Validate performs a request validation based
// on the data source URL and some of the request
// attributes (headers, cookies, etc).
Validate(dsURL string, req *http.Request) error
}
...@@ -51,12 +51,13 @@ type Manager interface { ...@@ -51,12 +51,13 @@ type Manager interface {
} }
type manager struct { type manager struct {
Cfg *setting.Cfg `inject:""` Cfg *setting.Cfg `inject:""`
License models.Licensing `inject:""` License models.Licensing `inject:""`
pluginsMu sync.RWMutex PluginRequestValidator models.PluginRequestValidator `inject:""`
plugins map[string]Plugin pluginsMu sync.RWMutex
logger log.Logger plugins map[string]Plugin
pluginSettings map[string]pluginSettings logger log.Logger
pluginSettings map[string]pluginSettings
} }
func (m *manager) Init() error { func (m *manager) Init() error {
...@@ -195,6 +196,19 @@ func (m *manager) CollectMetrics(ctx context.Context, pluginID string) (*backend ...@@ -195,6 +196,19 @@ func (m *manager) CollectMetrics(ctx context.Context, pluginID string) (*backend
// CheckHealth checks the health of a registered backend plugin. // CheckHealth checks the health of a registered backend plugin.
func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginContext) (*backend.CheckHealthResult, error) { func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginContext) (*backend.CheckHealthResult, error) {
var dsURL string
if pluginContext.DataSourceInstanceSettings != nil {
dsURL = pluginContext.DataSourceInstanceSettings.URL
}
err := m.PluginRequestValidator.Validate(dsURL, nil)
if err != nil {
return &backend.CheckHealthResult{
Status: http.StatusForbidden,
Message: "Access denied",
}, nil
}
m.pluginsMu.RLock() m.pluginsMu.RLock()
p, registered := m.plugins[pluginContext.PluginID] p, registered := m.plugins[pluginContext.PluginID]
m.pluginsMu.RUnlock() m.pluginsMu.RUnlock()
...@@ -204,7 +218,7 @@ func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginC ...@@ -204,7 +218,7 @@ func (m *manager) CheckHealth(ctx context.Context, pluginContext backend.PluginC
} }
var resp *backend.CheckHealthResult var resp *backend.CheckHealthResult
err := instrumentCheckHealthRequest(p.PluginID(), func() (innerErr error) { err = instrumentCheckHealthRequest(p.PluginID(), func() (innerErr error) {
resp, innerErr = p.CheckHealth(ctx, &backend.CheckHealthRequest{PluginContext: pluginContext}) resp, innerErr = p.CheckHealth(ctx, &backend.CheckHealthRequest{PluginContext: pluginContext})
return return
}) })
...@@ -289,6 +303,17 @@ func (m *manager) callResourceInternal(w http.ResponseWriter, req *http.Request, ...@@ -289,6 +303,17 @@ func (m *manager) callResourceInternal(w http.ResponseWriter, req *http.Request,
// CallResource calls a plugin resource. // CallResource calls a plugin resource.
func (m *manager) CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string) { func (m *manager) CallResource(pCtx backend.PluginContext, reqCtx *models.ReqContext, path string) {
var dsURL string
if pCtx.DataSourceInstanceSettings != nil {
dsURL = pCtx.DataSourceInstanceSettings.URL
}
err := m.PluginRequestValidator.Validate(dsURL, reqCtx.Req.Request)
if err != nil {
reqCtx.JsonApiErr(http.StatusForbidden, "Access denied", err)
return
}
clonedReq := reqCtx.Req.Clone(reqCtx.Req.Context()) clonedReq := reqCtx.Req.Clone(reqCtx.Req.Context())
rawURL := path rawURL := path
if clonedReq.URL.RawQuery != "" { if clonedReq.URL.RawQuery != "" {
......
...@@ -279,12 +279,14 @@ func newManagerScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *m ...@@ -279,12 +279,14 @@ func newManagerScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *m
t.Helper() t.Helper()
cfg := setting.NewCfg() cfg := setting.NewCfg()
license := &testLicensingService{} license := &testLicensingService{}
validator := &testPluginRequestValidator{}
ctx := &managerScenarioCtx{ ctx := &managerScenarioCtx{
cfg: cfg, cfg: cfg,
license: license, license: license,
manager: &manager{ manager: &manager{
Cfg: cfg, Cfg: cfg,
License: license, License: license,
PluginRequestValidator: validator,
}, },
} }
...@@ -418,3 +420,9 @@ func (t *testLicensingService) HasValidLicense() bool { ...@@ -418,3 +420,9 @@ func (t *testLicensingService) HasValidLicense() bool {
func (t *testLicensingService) Environment() map[string]string { func (t *testLicensingService) Environment() map[string]string {
return map[string]string{"GF_ENTERPRISE_LICENSE_TEXT": t.tokenRaw} return map[string]string{"GF_ENTERPRISE_LICENSE_TEXT": t.tokenRaw}
} }
type testPluginRequestValidator struct{}
func (t *testPluginRequestValidator) Validate(string, *http.Request) error {
return nil
}
...@@ -6,11 +6,12 @@ package pluginextensionv2 ...@@ -6,11 +6,12 @@ package pluginextensionv2
import ( import (
context "context" context "context"
fmt "fmt" fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto" proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
math "math"
) )
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
......
...@@ -119,6 +119,11 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange * ...@@ -119,6 +119,11 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
return nil, fmt.Errorf("could not find datasource: %w", err) return nil, fmt.Errorf("could not find datasource: %w", err)
} }
err := context.RequestValidator.Validate(getDsInfo.Result.Url, nil)
if err != nil {
return nil, fmt.Errorf("access denied: %w", err)
}
req := c.getRequestForAlertRule(getDsInfo.Result, timeRange, context.IsDebug) req := c.getRequestForAlertRule(getDsInfo.Result, timeRange, context.IsDebug)
result := make(tsdb.TimeSeriesSlice, 0) result := make(tsdb.TimeSeriesSlice, 0)
......
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana-plugin-sdk-go/data"
...@@ -235,7 +237,8 @@ func queryConditionScenario(desc string, fn queryConditionScenarioFunc) { ...@@ -235,7 +237,8 @@ func queryConditionScenario(desc string, fn queryConditionScenarioFunc) {
ctx := &queryConditionTestContext{} ctx := &queryConditionTestContext{}
ctx.result = &alerting.EvalContext{ ctx.result = &alerting.EvalContext{
Rule: &alerting.Rule{}, Rule: &alerting.Rule{},
RequestValidator: &validations.OSSPluginRequestValidator{},
} }
fn(ctx) fn(ctx)
......
...@@ -6,16 +6,16 @@ import ( ...@@ -6,16 +6,16 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
tlog "github.com/opentracing/opentracing-go/log"
"github.com/benbjohnson/clock" "github.com/benbjohnson/clock"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
tlog "github.com/opentracing/opentracing-go/log"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
...@@ -23,8 +23,9 @@ import ( ...@@ -23,8 +23,9 @@ import (
// schedules alert evaluations and makes sure notifications // schedules alert evaluations and makes sure notifications
// are sent. // are sent.
type AlertEngine struct { type AlertEngine struct {
RenderService rendering.Service `inject:""` RenderService rendering.Service `inject:""`
Bus bus.Bus `inject:""` Bus bus.Bus `inject:""`
RequestValidator models.PluginRequestValidator `inject:""`
execQueue chan *Job execQueue chan *Job
ticker *Ticker ticker *Ticker
...@@ -164,7 +165,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan ...@@ -164,7 +165,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
span := opentracing.StartSpan("alert execution") span := opentracing.StartSpan("alert execution")
alertCtx = opentracing.ContextWithSpan(alertCtx, span) alertCtx = opentracing.ContextWithSpan(alertCtx, span)
evalContext := NewEvalContext(alertCtx, job.Rule) evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator)
evalContext.Ctx = alertCtx evalContext.Ctx = alertCtx
go func() { go func() {
......
...@@ -33,19 +33,22 @@ type EvalContext struct { ...@@ -33,19 +33,22 @@ type EvalContext struct {
NoDataFound bool NoDataFound bool
PrevAlertState models.AlertStateType PrevAlertState models.AlertStateType
RequestValidator models.PluginRequestValidator
Ctx context.Context Ctx context.Context
} }
// NewEvalContext is the EvalContext constructor. // NewEvalContext is the EvalContext constructor.
func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext { func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator) *EvalContext {
return &EvalContext{ return &EvalContext{
Ctx: alertCtx, Ctx: alertCtx,
StartTime: time.Now(), StartTime: time.Now(),
Rule: rule, Rule: rule,
Logs: make([]*ResultLogEntry, 0), Logs: make([]*ResultLogEntry, 0),
EvalMatches: make([]*EvalMatch, 0), EvalMatches: make([]*EvalMatch, 0),
log: log.New("alerting.evalContext"), log: log.New("alerting.evalContext"),
PrevAlertState: rule.State, PrevAlertState: rule.State,
RequestValidator: requestValidator,
} }
} }
......
...@@ -6,6 +6,8 @@ import ( ...@@ -6,6 +6,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
...@@ -13,7 +15,7 @@ import ( ...@@ -13,7 +15,7 @@ import (
) )
func TestStateIsUpdatedWhenNeeded(t *testing.T) { func TestStateIsUpdatedWhenNeeded(t *testing.T) {
ctx := NewEvalContext(context.TODO(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}) ctx := NewEvalContext(context.TODO(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{})
t.Run("ok -> alerting", func(t *testing.T) { t.Run("ok -> alerting", func(t *testing.T) {
ctx.PrevAlertState = models.AlertStateOK ctx.PrevAlertState = models.AlertStateOK
...@@ -198,7 +200,7 @@ func TestGetStateFromEvalContext(t *testing.T) { ...@@ -198,7 +200,7 @@ func TestGetStateFromEvalContext(t *testing.T) {
} }
for _, tc := range tcs { for _, tc := range tcs {
evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}) evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{})
tc.applyFn(evalContext) tc.applyFn(evalContext)
newState := evalContext.GetNewState() newState := evalContext.GetNewState()
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
...@@ -27,7 +29,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -27,7 +29,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
Conditions: []Condition{&conditionStub{ Conditions: []Condition{&conditionStub{
firing: true, firing: true,
}}, }},
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, true) So(context.Firing, ShouldEqual, true)
...@@ -37,7 +39,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -37,7 +39,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
Convey("Show return triggered with single passing condition2", func() { Convey("Show return triggered with single passing condition2", func() {
context := NewEvalContext(context.TODO(), &Rule{ context := NewEvalContext(context.TODO(), &Rule{
Conditions: []Condition{&conditionStub{firing: true, operator: "and"}}, Conditions: []Condition{&conditionStub{firing: true, operator: "and"}},
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, true) So(context.Firing, ShouldEqual, true)
...@@ -50,7 +52,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -50,7 +52,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}}, &conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}},
&conditionStub{firing: false, operator: "and"}, &conditionStub{firing: false, operator: "and"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -63,7 +65,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -63,7 +65,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"}, &conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, true) So(context.Firing, ShouldEqual, true)
...@@ -76,7 +78,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -76,7 +78,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"}, &conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "and"}, &conditionStub{firing: false, operator: "and"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -90,7 +92,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -90,7 +92,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"}, &conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, true) So(context.Firing, ShouldEqual, true)
...@@ -104,7 +106,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -104,7 +106,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "and"}, &conditionStub{firing: false, operator: "and"},
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -118,7 +120,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -118,7 +120,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "and"}, &conditionStub{firing: false, operator: "and"},
&conditionStub{firing: true, operator: "and"}, &conditionStub{firing: true, operator: "and"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -132,7 +134,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -132,7 +134,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
&conditionStub{firing: true, operator: "or"}, &conditionStub{firing: true, operator: "or"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, true) So(context.Firing, ShouldEqual, true)
...@@ -146,7 +148,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -146,7 +148,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
&conditionStub{firing: false, operator: "or"}, &conditionStub{firing: false, operator: "or"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -161,7 +163,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -161,7 +163,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "or", noData: false}, &conditionStub{operator: "or", noData: false},
&conditionStub{operator: "or", noData: false}, &conditionStub{operator: "or", noData: false},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.NoDataFound, ShouldBeFalse) So(context.NoDataFound, ShouldBeFalse)
...@@ -172,7 +174,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -172,7 +174,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
Conditions: []Condition{ Conditions: []Condition{
&conditionStub{operator: "and", noData: true}, &conditionStub{operator: "and", noData: true},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.Firing, ShouldEqual, false) So(context.Firing, ShouldEqual, false)
...@@ -185,7 +187,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -185,7 +187,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "and", noData: true}, &conditionStub{operator: "and", noData: true},
&conditionStub{operator: "and", noData: false}, &conditionStub{operator: "and", noData: false},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.NoDataFound, ShouldBeFalse) So(context.NoDataFound, ShouldBeFalse)
...@@ -197,7 +199,7 @@ func TestAlertingEvaluationHandler(t *testing.T) { ...@@ -197,7 +199,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "or", noData: true}, &conditionStub{operator: "or", noData: true},
&conditionStub{operator: "or", noData: false}, &conditionStub{operator: "or", noData: false},
}, },
}) }, &validations.OSSPluginRequestValidator{})
handler.Eval(context) handler.Eval(context)
So(context.NoDataFound, ShouldBeTrue) So(context.NoDataFound, ShouldBeTrue)
......
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
...@@ -19,17 +21,17 @@ import ( ...@@ -19,17 +21,17 @@ import (
func TestNotificationService(t *testing.T) { func TestNotificationService(t *testing.T) {
testRule := &Rule{Name: "Test", Message: "Something is bad"} testRule := &Rule{Name: "Test", Message: "Something is bad"}
evalCtx := NewEvalContext(context.Background(), testRule) evalCtx := NewEvalContext(context.Background(), testRule, &validations.OSSPluginRequestValidator{})
testRuleTemplated := &Rule{Name: "Test latency ${quantile}", Message: "Something is bad on instance ${instance}"} testRuleTemplated := &Rule{Name: "Test latency ${quantile}", Message: "Something is bad on instance ${instance}"}
evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated) evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{})
evalCtxWithMatch.EvalMatches = []*EvalMatch{{ evalCtxWithMatch.EvalMatches = []*EvalMatch{{
Tags: map[string]string{ Tags: map[string]string{
"instance": "localhost:3000", "instance": "localhost:3000",
"quantile": "0.99", "quantile": "0.99",
}, },
}} }}
evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated) evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{})
notificationServiceScenario(t, "Given alert rule with upload image enabled should render and upload image and send notification", notificationServiceScenario(t, "Given alert rule with upload image enabled should render and upload image and send notification",
evalCtx, true, func(sc *scenarioContext) { evalCtx, true, func(sc *scenarioContext) {
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
...@@ -66,7 +68,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) { ...@@ -66,7 +68,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {
am := &AlertmanagerNotifier{log: log.New("test.logger")} am := &AlertmanagerNotifier{log: log.New("test.logger")}
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState, State: tc.prevState,
}) }, &validations.OSSPluginRequestValidator{})
evalContext.Rule.State = tc.newState evalContext.Rule.State = tc.newState
......
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
...@@ -169,7 +171,7 @@ func TestShouldSendAlertNotification(t *testing.T) { ...@@ -169,7 +171,7 @@ func TestShouldSendAlertNotification(t *testing.T) {
for _, tc := range tcs { for _, tc := range tcs {
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{ evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState, State: tc.prevState,
}) }, &validations.OSSPluginRequestValidator{})
if tc.state == nil { if tc.state == nil {
tc.state = &models.AlertNotificationState{} tc.state = &models.AlertNotificationState{}
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/alerting"
...@@ -48,7 +50,7 @@ func TestDingDingNotifier(t *testing.T) { ...@@ -48,7 +50,7 @@ func TestDingDingNotifier(t *testing.T) {
&alerting.Rule{ &alerting.Rule{
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
Message: `{host="localhost"}`, Message: `{host="localhost"}`,
}) }, &validations.OSSPluginRequestValidator{})
_, err = notifier.genBody(evalContext, "") _, err = notifier.genBody(evalContext, "")
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
...@@ -78,7 +80,7 @@ func TestOpsGenieNotifier(t *testing.T) { ...@@ -78,7 +80,7 @@ func TestOpsGenieNotifier(t *testing.T) {
Message: "someMessage", Message: "someMessage",
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
AlertRuleTags: tagPairs, AlertRuleTags: tagPairs,
}) }, &validations.OSSPluginRequestValidator{})
evalContext.IsTestRun = true evalContext.IsTestRun = true
receivedTags := make([]string, 0) receivedTags := make([]string, 0)
......
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
...@@ -138,7 +140,7 @@ func TestPagerdutyNotifier(t *testing.T) { ...@@ -138,7 +140,7 @@ func TestPagerdutyNotifier(t *testing.T) {
Name: "someRule", Name: "someRule",
Message: "someMessage", Message: "someMessage",
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
}) }, &validations.OSSPluginRequestValidator{})
evalContext.IsTestRun = true evalContext.IsTestRun = true
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext) payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
...@@ -194,7 +196,7 @@ func TestPagerdutyNotifier(t *testing.T) { ...@@ -194,7 +196,7 @@ func TestPagerdutyNotifier(t *testing.T) {
Name: "someRule", Name: "someRule",
Message: "someMessage", Message: "someMessage",
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
}) }, &validations.OSSPluginRequestValidator{})
evalContext.IsTestRun = true evalContext.IsTestRun = true
evalContext.EvalMatches = []*alerting.EvalMatch{ evalContext.EvalMatches = []*alerting.EvalMatch{
{ {
...@@ -272,7 +274,7 @@ func TestPagerdutyNotifier(t *testing.T) { ...@@ -272,7 +274,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "severity", Value: "warning"}, {Key: "severity", Value: "warning"},
{Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)}, {Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)},
}, },
}) }, &validations.OSSPluginRequestValidator{})
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true evalContext.IsTestRun = true
...@@ -350,7 +352,7 @@ func TestPagerdutyNotifier(t *testing.T) { ...@@ -350,7 +352,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "component", Value: "aComponent"}, {Key: "component", Value: "aComponent"},
{Key: "severity", Value: "info"}, {Key: "severity", Value: "info"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true evalContext.IsTestRun = true
...@@ -428,7 +430,7 @@ func TestPagerdutyNotifier(t *testing.T) { ...@@ -428,7 +430,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "component", Value: "aComponent"}, {Key: "component", Value: "aComponent"},
{Key: "severity", Value: "llama"}, {Key: "severity", Value: "llama"},
}, },
}) }, &validations.OSSPluginRequestValidator{})
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png" evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true evalContext.IsTestRun = true
......
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
...@@ -75,7 +77,7 @@ func TestGenPushoverBody(t *testing.T) { ...@@ -75,7 +77,7 @@ func TestGenPushoverBody(t *testing.T) {
evalContext := alerting.NewEvalContext(context.Background(), evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{ &alerting.Rule{
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
}) }, &validations.OSSPluginRequestValidator{})
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "") _, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
So(err, ShouldBeNil) So(err, ShouldBeNil)
...@@ -86,7 +88,7 @@ func TestGenPushoverBody(t *testing.T) { ...@@ -86,7 +88,7 @@ func TestGenPushoverBody(t *testing.T) {
evalContext := alerting.NewEvalContext(context.Background(), evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{ &alerting.Rule{
State: models.AlertStateOK, State: models.AlertStateOK,
}) }, &validations.OSSPluginRequestValidator{})
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "") _, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
So(err, ShouldBeNil) So(err, ShouldBeNil)
......
...@@ -4,6 +4,8 @@ import ( ...@@ -4,6 +4,8 @@ import (
"context" "context"
"testing" "testing"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting" "github.com/grafana/grafana/pkg/services/alerting"
...@@ -57,7 +59,7 @@ func TestTelegramNotifier(t *testing.T) { ...@@ -57,7 +59,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm", Name: "This is an alarm",
Message: "Some kind of message.", Message: "Some kind of message.",
State: models.AlertStateOK, State: models.AlertStateOK,
}) }, &validations.OSSPluginRequestValidator{})
caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "") caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "")
So(len(caption), ShouldBeLessThanOrEqualTo, 1024) So(len(caption), ShouldBeLessThanOrEqualTo, 1024)
...@@ -73,7 +75,7 @@ func TestTelegramNotifier(t *testing.T) { ...@@ -73,7 +75,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm", Name: "This is an alarm",
Message: "Some kind of message.", Message: "Some kind of message.",
State: models.AlertStateOK, State: models.AlertStateOK,
}) }, &validations.OSSPluginRequestValidator{})
caption := generateImageCaption(evalContext, caption := generateImageCaption(evalContext,
"http://grafa.url/abcdefaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "http://grafa.url/abcdefaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
...@@ -91,7 +93,7 @@ func TestTelegramNotifier(t *testing.T) { ...@@ -91,7 +93,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm", Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis scelerisque. Nulla ipsum ex, iaculis vitae vehicula sit amet, fermentum eu eros.", Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis scelerisque. Nulla ipsum ex, iaculis vitae vehicula sit amet, fermentum eu eros.",
State: models.AlertStateOK, State: models.AlertStateOK,
}) }, &validations.OSSPluginRequestValidator{})
caption := generateImageCaption(evalContext, caption := generateImageCaption(evalContext,
"http://grafa.url/foo", "http://grafa.url/foo",
...@@ -108,7 +110,7 @@ func TestTelegramNotifier(t *testing.T) { ...@@ -108,7 +110,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm", Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis sceleri", Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis sceleri",
State: models.AlertStateOK, State: models.AlertStateOK,
}) }, &validations.OSSPluginRequestValidator{})
caption := generateImageCaption(evalContext, caption := generateImageCaption(evalContext,
"http://grafa.url/foo", "http://grafa.url/foo",
......
...@@ -3,6 +3,7 @@ package alerting ...@@ -3,6 +3,7 @@ package alerting
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/grafana/grafana/pkg/components/securejsondata" "github.com/grafana/grafana/pkg/components/securejsondata"
...@@ -83,7 +84,7 @@ func createTestEvalContext(cmd *NotificationTestCommand) *EvalContext { ...@@ -83,7 +84,7 @@ func createTestEvalContext(cmd *NotificationTestCommand) *EvalContext {
State: models.AlertStateAlerting, State: models.AlertStateAlerting,
} }
ctx := NewEvalContext(context.Background(), testRule) ctx := NewEvalContext(context.Background(), testRule, fakeRequestValidator{})
if cmd.Settings.Get("uploadImage").MustBool(true) { if cmd.Settings.Get("uploadImage").MustBool(true) {
ctx.ImagePublicURL = "https://grafana.com/assets/img/blog/mixed_styles.png" ctx.ImagePublicURL = "https://grafana.com/assets/img/blog/mixed_styles.png"
} }
...@@ -109,3 +110,9 @@ func evalMatchesBasedOnState() []*EvalMatch { ...@@ -109,3 +110,9 @@ func evalMatchesBasedOnState() []*EvalMatch {
return matches return matches
} }
type fakeRequestValidator struct{}
func (fakeRequestValidator) Validate(_ string, _ *http.Request) error {
return nil
}
...@@ -51,7 +51,7 @@ func handleAlertTestCommand(cmd *AlertTestCommand) error { ...@@ -51,7 +51,7 @@ func handleAlertTestCommand(cmd *AlertTestCommand) error {
func testAlertRule(rule *Rule) *EvalContext { func testAlertRule(rule *Rule) *EvalContext {
handler := NewEvalHandler() handler := NewEvalHandler()
context := NewEvalContext(context.Background(), rule) context := NewEvalContext(context.Background(), rule, fakeRequestValidator{})
context.IsTestRun = true context.IsTestRun = true
context.IsDebug = true context.IsDebug = true
......
package validations
import (
"net/http"
)
type OSSPluginRequestValidator struct{}
func (*OSSPluginRequestValidator) Init() error {
return nil
}
func (*OSSPluginRequestValidator) Validate(string, *http.Request) error {
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment