diff --git a/pkg/expr/graph.go b/pkg/expr/graph.go index 26c1e5b885d..b8b7d1bce45 100644 --- a/pkg/expr/graph.go +++ b/pkg/expr/graph.go @@ -166,11 +166,13 @@ func (s *Service) buildGraph(req *Request) (*simple.DirectedGraph, error) { } var node Node - - if IsDataSource(rn.DataSource.UID) { - node, err = buildCMDNode(dp, rn) - } else { + switch NodeTypeFromDatasourceUID(query.DataSource.UID) { + case TypeDatasourceNode: node, err = s.buildDSNode(dp, rn, req) + case TypeCMDNode: + node, err = buildCMDNode(dp, rn) + default: + err = fmt.Errorf("unsupported node type '%s'", NodeTypeFromDatasourceUID(query.DataSource.UID)) } if err != nil { diff --git a/pkg/expr/graph_test.go b/pkg/expr/graph_test.go index f028da543fd..25dbc57eca8 100644 --- a/pkg/expr/graph_test.go +++ b/pkg/expr/graph_test.go @@ -22,7 +22,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "B", "reducer": "mean", @@ -46,7 +46,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "$B", "type": "math" @@ -54,7 +54,7 @@ func TestServicebuildPipeLine(t *testing.T) { }, { RefID: "B", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "$A", "type": "math" @@ -70,7 +70,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "$A", "type": "math" @@ -86,7 +86,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "$B", "type": "math" @@ -102,7 +102,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "type": "classic_conditions", "conditions": [ @@ -133,7 +133,7 @@ func TestServicebuildPipeLine(t *testing.T) { }, { RefID: "B", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "C", "reducer": "mean", @@ -157,7 +157,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "type": "classic_conditions", "conditions": [ @@ -188,7 +188,7 @@ func TestServicebuildPipeLine(t *testing.T) { }, { RefID: "B", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "A", "reducer": "mean", @@ -212,7 +212,7 @@ func TestServicebuildPipeLine(t *testing.T) { Queries: []Query{ { RefID: "A", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "expression": "B", "reducer": "mean", diff --git a/pkg/expr/service.go b/pkg/expr/service.go index b856eb1a3dc..16d625fc9a3 100644 --- a/pkg/expr/service.go +++ b/pkg/expr/service.go @@ -2,6 +2,8 @@ package expr import ( "context" + "errors" + "fmt" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -39,6 +41,15 @@ func IsDataSource(uid string) bool { return uid == DatasourceUID || uid == OldDatasourceUID } +// NodeTypeFromDatasourceUID returns NodeType depending on the UID of the data source: TypeCMDNode if UID is DatasourceUID +// or OldDatasourceUID, and TypeDatasourceNode otherwise. +func NodeTypeFromDatasourceUID(uid string) NodeType { + if IsDataSource(uid) { + return TypeCMDNode + } + return TypeDatasourceNode +} + // Service is service representation for expression handling. type Service struct { cfg *setting.Cfg @@ -46,6 +57,8 @@ type Service struct { pCtxProvider *plugincontext.Provider features featuremgmt.FeatureToggles + pluginsClient backend.CallResourceHandler + tracer tracing.Tracer metrics *metrics } @@ -53,12 +66,13 @@ type Service struct { func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, pCtxProvider *plugincontext.Provider, features featuremgmt.FeatureToggles, registerer prometheus.Registerer, tracer tracing.Tracer) *Service { return &Service{ - cfg: cfg, - dataService: pluginClient, - pCtxProvider: pCtxProvider, - features: features, - tracer: tracer, - metrics: newMetrics(registerer), + cfg: cfg, + dataService: pluginClient, + pCtxProvider: pCtxProvider, + features: features, + tracer: tracer, + metrics: newMetrics(registerer), + pluginsClient: pluginClient, } } @@ -91,13 +105,27 @@ func (s *Service) ExecutePipeline(ctx context.Context, now time.Time, pipeline D return res, nil } -func DataSourceModel() *datasources.DataSource { - return &datasources.DataSource{ - ID: DatasourceID, - UID: DatasourceUID, - Name: DatasourceUID, - Type: DatasourceType, - JsonData: simplejson.New(), - SecureJsonData: make(map[string][]byte), +// Create a datasources.DataSource struct from NodeType. Returns error if kind is TypeDatasourceNode or unknown one. +func DataSourceModelFromNodeType(kind NodeType) (*datasources.DataSource, error) { + switch kind { + case TypeCMDNode: + return &datasources.DataSource{ + ID: DatasourceID, + UID: DatasourceUID, + Name: DatasourceUID, + Type: DatasourceType, + JsonData: simplejson.New(), + SecureJsonData: make(map[string][]byte), + }, nil + case TypeDatasourceNode: + return nil, errors.New("cannot create expression data source for data source kind") + default: + return nil, fmt.Errorf("cannot create expression data source for '%s' kind", kind) } } + +// Deprecated. Use DataSourceModelFromNodeType instead +func DataSourceModel() *datasources.DataSource { + d, _ := DataSourceModelFromNodeType(TypeCMDNode) + return d +} diff --git a/pkg/expr/service_test.go b/pkg/expr/service_test.go index cd0314d1992..4588ad83015 100644 --- a/pkg/expr/service_test.go +++ b/pkg/expr/service_test.go @@ -62,7 +62,7 @@ func TestService(t *testing.T) { }, { RefID: "B", - DataSource: DataSourceModel(), + DataSource: dataSourceModel(), JSON: json.RawMessage(`{ "datasource": { "uid": "__expr__", "type": "__expr__"}, "type": "math", "expression": "$A * 2" }`), }, } @@ -124,3 +124,8 @@ func (me *mockEndpoint) QueryData(ctx context.Context, req *backend.QueryDataReq } return resp, nil } + +func dataSourceModel() *datasources.DataSource { + d, _ := DataSourceModelFromNodeType(TypeCMDNode) + return d +} diff --git a/pkg/services/ngalert/eval/eval.go b/pkg/services/ngalert/eval/eval.go index e40a0f3140b..78339b97bb0 100644 --- a/pkg/services/ngalert/eval/eval.go +++ b/pkg/services/ngalert/eval/eval.go @@ -269,13 +269,14 @@ func getExprRequest(ctx EvaluationContext, data []models.AlertQuery, dsCacheServ ds, ok := datasources[q.DatasourceUID] if !ok { - if expr.IsDataSource(q.DatasourceUID) { - ds = expr.DataSourceModel() - } else { + switch nodeType := expr.NodeTypeFromDatasourceUID(q.DatasourceUID); nodeType { + case expr.TypeCMDNode: + ds, err = expr.DataSourceModelFromNodeType(nodeType) + case expr.TypeDatasourceNode: ds, err = dsCacheService.GetDatasourceByUID(ctx.Ctx, q.DatasourceUID, ctx.User, false /*skipCache*/) - if err != nil { - return nil, fmt.Errorf("failed to build query '%s': %w", q.RefID, err) - } + } + if err != nil { + return nil, fmt.Errorf("failed to build query '%s': %w", q.RefID, err) } datasources[q.DatasourceUID] = ds } @@ -621,7 +622,7 @@ func (e *evaluatorImpl) Validate(ctx EvaluationContext, condition models.Conditi return err } for _, query := range req.Queries { - if query.DataSource == nil || expr.IsDataSource(query.DataSource.UID) { + if query.DataSource == nil || expr.NodeTypeFromDatasourceUID(query.DataSource.UID) != expr.TypeDatasourceNode { continue } p, found := e.pluginsStore.Plugin(ctx.Ctx, query.DataSource.Type) diff --git a/pkg/services/ngalert/models/alert_query.go b/pkg/services/ngalert/models/alert_query.go index 6f6c437e72a..ac2812d3dd1 100644 --- a/pkg/services/ngalert/models/alert_query.go +++ b/pkg/services/ngalert/models/alert_query.go @@ -113,7 +113,7 @@ func (aq *AlertQuery) setModelProps() error { // IsExpression returns true if the alert query is an expression. func (aq *AlertQuery) IsExpression() (bool, error) { - return expr.IsDataSource(aq.DatasourceUID), nil + return expr.NodeTypeFromDatasourceUID(aq.DatasourceUID) == expr.TypeCMDNode, nil } // setMaxDatapoints sets the model maxDataPoints if it's missing or invalid diff --git a/pkg/services/query/query.go b/pkg/services/query/query.go index c67d74abb99..5b88dbdc13b 100644 --- a/pkg/services/query/query.go +++ b/pkg/services/query/query.go @@ -269,7 +269,7 @@ func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user *user.SignedI } datasourcesByUid[ds.UID] = ds - if expr.IsDataSource(ds.UID) { + if expr.NodeTypeFromDatasourceUID(ds.UID) != expr.TypeDatasourceNode { req.hasExpression = true } else { req.dsTypes[ds.Type] = true @@ -321,8 +321,8 @@ func (s *ServiceImpl) getDataSourceFromQuery(ctx context.Context, user *user.Sig return ds, nil } - if expr.IsDataSource(uid) { - return expr.DataSourceModel(), nil + if kind := expr.NodeTypeFromDatasourceUID(uid); kind != expr.TypeDatasourceNode { + return expr.DataSourceModelFromNodeType(kind) } if uid == grafanads.DatasourceUID {