SSE: Add functions that determine NodeType by UID and construct a data source struct from NodeType (#70106)

* add NodeTypeFromDatasourceUID and DataSourceModelFromNodeType()
* deprecate expr.DataSourceModel
* replace usages of IsDataSource to NodeTypeFromDatasourceUID 
* replace usages of DataSourceModel to DataSourceModelFromNodeType()
pull/70280/head
Yuri Tseretyan 3 years ago committed by GitHub
parent 3d15d54a71
commit 842f33580e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      pkg/expr/graph.go
  2. 20
      pkg/expr/graph_test.go
  3. 56
      pkg/expr/service.go
  4. 7
      pkg/expr/service_test.go
  5. 15
      pkg/services/ngalert/eval/eval.go
  6. 2
      pkg/services/ngalert/models/alert_query.go
  7. 6
      pkg/services/query/query.go

@ -166,11 +166,13 @@ func (s *Service) buildGraph(req *Request) (*simple.DirectedGraph, error) {
} }
var node Node var node Node
switch NodeTypeFromDatasourceUID(query.DataSource.UID) {
if IsDataSource(rn.DataSource.UID) { case TypeDatasourceNode:
node, err = buildCMDNode(dp, rn)
} else {
node, err = s.buildDSNode(dp, rn, req) 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 { if err != nil {

@ -22,7 +22,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "B", "expression": "B",
"reducer": "mean", "reducer": "mean",
@ -46,7 +46,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "$B", "expression": "$B",
"type": "math" "type": "math"
@ -54,7 +54,7 @@ func TestServicebuildPipeLine(t *testing.T) {
}, },
{ {
RefID: "B", RefID: "B",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "$A", "expression": "$A",
"type": "math" "type": "math"
@ -70,7 +70,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "$A", "expression": "$A",
"type": "math" "type": "math"
@ -86,7 +86,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "$B", "expression": "$B",
"type": "math" "type": "math"
@ -102,7 +102,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"type": "classic_conditions", "type": "classic_conditions",
"conditions": [ "conditions": [
@ -133,7 +133,7 @@ func TestServicebuildPipeLine(t *testing.T) {
}, },
{ {
RefID: "B", RefID: "B",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "C", "expression": "C",
"reducer": "mean", "reducer": "mean",
@ -157,7 +157,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"type": "classic_conditions", "type": "classic_conditions",
"conditions": [ "conditions": [
@ -188,7 +188,7 @@ func TestServicebuildPipeLine(t *testing.T) {
}, },
{ {
RefID: "B", RefID: "B",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "A", "expression": "A",
"reducer": "mean", "reducer": "mean",
@ -212,7 +212,7 @@ func TestServicebuildPipeLine(t *testing.T) {
Queries: []Query{ Queries: []Query{
{ {
RefID: "A", RefID: "A",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ JSON: json.RawMessage(`{
"expression": "B", "expression": "B",
"reducer": "mean", "reducer": "mean",

@ -2,6 +2,8 @@ package expr
import ( import (
"context" "context"
"errors"
"fmt"
"time" "time"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
@ -39,6 +41,15 @@ func IsDataSource(uid string) bool {
return uid == DatasourceUID || uid == OldDatasourceUID 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. // Service is service representation for expression handling.
type Service struct { type Service struct {
cfg *setting.Cfg cfg *setting.Cfg
@ -46,6 +57,8 @@ type Service struct {
pCtxProvider *plugincontext.Provider pCtxProvider *plugincontext.Provider
features featuremgmt.FeatureToggles features featuremgmt.FeatureToggles
pluginsClient backend.CallResourceHandler
tracer tracing.Tracer tracer tracing.Tracer
metrics *metrics metrics *metrics
} }
@ -53,12 +66,13 @@ type Service struct {
func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, pCtxProvider *plugincontext.Provider, func ProvideService(cfg *setting.Cfg, pluginClient plugins.Client, pCtxProvider *plugincontext.Provider,
features featuremgmt.FeatureToggles, registerer prometheus.Registerer, tracer tracing.Tracer) *Service { features featuremgmt.FeatureToggles, registerer prometheus.Registerer, tracer tracing.Tracer) *Service {
return &Service{ return &Service{
cfg: cfg, cfg: cfg,
dataService: pluginClient, dataService: pluginClient,
pCtxProvider: pCtxProvider, pCtxProvider: pCtxProvider,
features: features, features: features,
tracer: tracer, tracer: tracer,
metrics: newMetrics(registerer), metrics: newMetrics(registerer),
pluginsClient: pluginClient,
} }
} }
@ -91,13 +105,27 @@ func (s *Service) ExecutePipeline(ctx context.Context, now time.Time, pipeline D
return res, nil return res, nil
} }
func DataSourceModel() *datasources.DataSource { // Create a datasources.DataSource struct from NodeType. Returns error if kind is TypeDatasourceNode or unknown one.
return &datasources.DataSource{ func DataSourceModelFromNodeType(kind NodeType) (*datasources.DataSource, error) {
ID: DatasourceID, switch kind {
UID: DatasourceUID, case TypeCMDNode:
Name: DatasourceUID, return &datasources.DataSource{
Type: DatasourceType, ID: DatasourceID,
JsonData: simplejson.New(), UID: DatasourceUID,
SecureJsonData: make(map[string][]byte), 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
}

@ -62,7 +62,7 @@ func TestService(t *testing.T) {
}, },
{ {
RefID: "B", RefID: "B",
DataSource: DataSourceModel(), DataSource: dataSourceModel(),
JSON: json.RawMessage(`{ "datasource": { "uid": "__expr__", "type": "__expr__"}, "type": "math", "expression": "$A * 2" }`), 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 return resp, nil
} }
func dataSourceModel() *datasources.DataSource {
d, _ := DataSourceModelFromNodeType(TypeCMDNode)
return d
}

@ -269,13 +269,14 @@ func getExprRequest(ctx EvaluationContext, data []models.AlertQuery, dsCacheServ
ds, ok := datasources[q.DatasourceUID] ds, ok := datasources[q.DatasourceUID]
if !ok { if !ok {
if expr.IsDataSource(q.DatasourceUID) { switch nodeType := expr.NodeTypeFromDatasourceUID(q.DatasourceUID); nodeType {
ds = expr.DataSourceModel() case expr.TypeCMDNode:
} else { ds, err = expr.DataSourceModelFromNodeType(nodeType)
case expr.TypeDatasourceNode:
ds, err = dsCacheService.GetDatasourceByUID(ctx.Ctx, q.DatasourceUID, ctx.User, false /*skipCache*/) 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 datasources[q.DatasourceUID] = ds
} }
@ -621,7 +622,7 @@ func (e *evaluatorImpl) Validate(ctx EvaluationContext, condition models.Conditi
return err return err
} }
for _, query := range req.Queries { 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 continue
} }
p, found := e.pluginsStore.Plugin(ctx.Ctx, query.DataSource.Type) p, found := e.pluginsStore.Plugin(ctx.Ctx, query.DataSource.Type)

@ -113,7 +113,7 @@ func (aq *AlertQuery) setModelProps() error {
// IsExpression returns true if the alert query is an expression. // IsExpression returns true if the alert query is an expression.
func (aq *AlertQuery) IsExpression() (bool, error) { 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 // setMaxDatapoints sets the model maxDataPoints if it's missing or invalid

@ -269,7 +269,7 @@ func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user *user.SignedI
} }
datasourcesByUid[ds.UID] = ds datasourcesByUid[ds.UID] = ds
if expr.IsDataSource(ds.UID) { if expr.NodeTypeFromDatasourceUID(ds.UID) != expr.TypeDatasourceNode {
req.hasExpression = true req.hasExpression = true
} else { } else {
req.dsTypes[ds.Type] = true req.dsTypes[ds.Type] = true
@ -321,8 +321,8 @@ func (s *ServiceImpl) getDataSourceFromQuery(ctx context.Context, user *user.Sig
return ds, nil return ds, nil
} }
if expr.IsDataSource(uid) { if kind := expr.NodeTypeFromDatasourceUID(uid); kind != expr.TypeDatasourceNode {
return expr.DataSourceModel(), nil return expr.DataSourceModelFromNodeType(kind)
} }
if uid == grafanads.DatasourceUID { if uid == grafanads.DatasourceUID {

Loading…
Cancel
Save