mirror of
https://github.com/grafana/grafana.git
synced 2026-08-18 09:05:04 -05:00
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:
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user