Alerting: support alerting on data.Frame (that can be time series) (#22812)

data.Frame (that can be series) are converted to as tsdb.TimeSeriesSlice - so new backend plugins can be shimmed into existing alerting

use sdk v0.31.0
This commit is contained in:
Kyle Brandt
2020-03-18 10:30:07 -04:00
committed by GitHub
parent 44b7f3ea1c
commit 0e09a3fe15
15 changed files with 808 additions and 55 deletions
+24 -2
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/util/errutil"
)
func init() {
@@ -168,12 +169,30 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
return nil, fmt.Errorf("tsdb.HandleRequest() response error %v", v)
}
result = append(result, v.Series...)
// If there are dataframes but no series on the result
useDataframes := v.Dataframes != nil && (v.Series == nil || len(v.Series) == 0)
if useDataframes { // convert the dataframes to tsdb.TimeSeries
frames, err := tsdb.FramesFromBytes(v.Dataframes)
if err != nil {
return nil, errutil.Wrap("tsdb.HandleRequest() failed to unmarshal arrow dataframes from bytes", err)
}
for _, frame := range frames {
ss, err := tsdb.FrameToSeriesSlice(frame)
if err != nil {
return nil, errutil.Wrapf(err, `tsdb.HandleRequest() failed to convert dataframe "%v" to tsdb.TimeSeriesSlice`, frame.Name)
}
result = append(result, ss...)
}
} else {
result = append(result, v.Series...)
}
queryResultData := map[string]interface{}{}
if context.IsTestRun {
queryResultData["series"] = v.Series
queryResultData["series"] = result
}
if context.IsDebug && v.Meta != nil {
@@ -181,6 +200,9 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
}
if context.IsTestRun || context.IsDebug {
if useDataframes {
queryResultData["fromDataframe"] = true
}
context.Logs = append(context.Logs, &alerting.ResultLogEntry{
Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
Data: simplejson.NewFromAny(queryResultData),
+40 -1
View File
@@ -3,7 +3,9 @@ package conditions
import (
"context"
"testing"
"time"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
@@ -51,6 +53,17 @@ func TestQueryCondition(t *testing.T) {
So(cr.Firing, ShouldBeTrue)
})
Convey("should fire when avg is above 100 on dataframe", func() {
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now()}),
data.NewField("val", nil, []int64{120, 150}),
)
cr, err := ctx.exec()
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
Convey("Should not fire when avg is below 100", func() {
points := tsdb.NewTimeSeriesPointsFromArgs(90, 0)
ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)}
@@ -60,6 +73,17 @@ func TestQueryCondition(t *testing.T) {
So(cr.Firing, ShouldBeFalse)
})
Convey("Should not fire when avg is below 100 on dataframe", func() {
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now()}),
data.NewField("val", nil, []int64{12, 47}),
)
cr, err := ctx.exec()
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeFalse)
})
Convey("Should fire if only first serie matches", func() {
ctx.series = tsdb.TimeSeriesSlice{
tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs(120, 0)),
@@ -144,6 +168,7 @@ type queryConditionTestContext struct {
reducer string
evaluator string
series tsdb.TimeSeriesSlice
frame *data.Frame
result *alerting.EvalContext
condition *QueryCondition
}
@@ -168,10 +193,24 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
ctx.condition = condition
qr := &tsdb.QueryResult{
Series: ctx.series,
}
if ctx.frame != nil {
bFrame, err := data.MarshalArrow(ctx.frame)
if err != nil {
return nil, err
}
qr = &tsdb.QueryResult{
Dataframes: [][]byte{bFrame},
}
}
condition.HandleRequest = func(context context.Context, dsInfo *models.DataSource, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
return &tsdb.Response{
Results: map[string]*tsdb.QueryResult{
"A": {Series: ctx.series},
"A": qr,
},
}, nil
}