From fb7f2ae2e2cdb8ffb9425c2fed2912097e5a6cc4 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 16 Aug 2016 12:34:54 +0200 Subject: [PATCH] feat(alerting): adds within_range and outside_range evaluators closes #5829 --- pkg/services/alerting/conditions/evaluator.go | 57 ++++++++++++++++--- .../alerting/conditions/evaluator_test.go | 45 +++++++++++++++ pkg/services/alerting/conditions/query.go | 2 +- 3 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 pkg/services/alerting/conditions/evaluator_test.go diff --git a/pkg/services/alerting/conditions/evaluator.go b/pkg/services/alerting/conditions/evaluator.go index 3ef8576afc7..3eeaf2e9c68 100644 --- a/pkg/services/alerting/conditions/evaluator.go +++ b/pkg/services/alerting/conditions/evaluator.go @@ -28,11 +28,30 @@ func (e *DefaultAlertEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float return false } -func NewDefaultAlertEvaluator(model *simplejson.Json) (*DefaultAlertEvaluator, error) { - evaluator := &DefaultAlertEvaluator{} +type RangedAlertEvaluator struct { + Type string + Lower float64 + Upper float64 +} - evaluator.Type = model.Get("type").MustString() - if evaluator.Type == "" { +func (e *RangedAlertEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool { + switch e.Type { + case "within_range": + return (e.Lower < reducedValue && e.Upper > reducedValue) || (e.Upper < reducedValue && e.Lower > reducedValue) + case "outside_range": + return (e.Upper < reducedValue && e.Lower < reducedValue) || (e.Upper > reducedValue && e.Lower > reducedValue) + } + + return false +} + +func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) { + defaultTypes := []string{"gt", "lt"} + rangedTypes := []string{"within_range", "outside_range"} + + typ := model.Get("type").MustString() + + if typ == "" { return nil, alerting.ValidationError{Reason: "Evaluator missing type property"} } @@ -41,11 +60,35 @@ func NewDefaultAlertEvaluator(model *simplejson.Json) (*DefaultAlertEvaluator, e return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"} } - threshold, ok := params[0].(json.Number) + firstParam, ok := params[0].(json.Number) if !ok { return nil, alerting.ValidationError{Reason: "Evaluator has invalid threshold parameter"} } - evaluator.Threshold, _ = threshold.Float64() - return evaluator, nil + if stringInSlice(typ, defaultTypes) { + evaluator := &DefaultAlertEvaluator{Type: typ} + evaluator.Threshold, _ = firstParam.Float64() + return evaluator, nil + } else if stringInSlice(typ, rangedTypes) { + secondParam, ok := params[1].(json.Number) + if !ok { + return nil, alerting.ValidationError{Reason: "Evaluator has invalid threshold parameter"} + } + + evaluator := &RangedAlertEvaluator{Type: typ} + evaluator.Lower, _ = firstParam.Float64() + evaluator.Upper, _ = secondParam.Float64() + return evaluator, nil + } + + return nil, alerting.ValidationError{Reason: "Evaludator invalid evaluator type"} +} + +func stringInSlice(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false } diff --git a/pkg/services/alerting/conditions/evaluator_test.go b/pkg/services/alerting/conditions/evaluator_test.go new file mode 100644 index 00000000000..3d9a53fcb29 --- /dev/null +++ b/pkg/services/alerting/conditions/evaluator_test.go @@ -0,0 +1,45 @@ +package conditions + +import ( + "testing" + + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/tsdb" + . "github.com/smartystreets/goconvey/convey" +) + +func test(json string, reducedValue float64) bool { + jsonModel, err := simplejson.NewJson([]byte(json)) + So(err, ShouldBeNil) + + evaluator, err := NewAlertEvaluator(jsonModel) + So(err, ShouldBeNil) + + return evaluator.Eval(&tsdb.TimeSeries{}, reducedValue) +} + +func TestEvalutors(t *testing.T) { + Convey("greater then", t, func() { + So(test(`{"type": "gt", "params": [1] }`, 3), ShouldBeTrue) + So(test(`{"type": "gt", "params": [3] }`, 1), ShouldBeFalse) + }) + + Convey("less then", t, func() { + So(test(`{"type": "lt", "params": [1] }`, 3), ShouldBeFalse) + So(test(`{"type": "lt", "params": [3] }`, 1), ShouldBeTrue) + }) + + Convey("within_range", t, func() { + So(test(`{"type": "within_range", "params": [1, 100] }`, 3), ShouldBeTrue) + So(test(`{"type": "within_range", "params": [1, 100] }`, 300), ShouldBeFalse) + So(test(`{"type": "within_range", "params": [100, 1] }`, 3), ShouldBeTrue) + So(test(`{"type": "within_range", "params": [100, 1] }`, 300), ShouldBeFalse) + }) + + Convey("outside_range", t, func() { + So(test(`{"type": "outside_range", "params": [1, 100] }`, 1000), ShouldBeTrue) + So(test(`{"type": "outside_range", "params": [1, 100] }`, 50), ShouldBeFalse) + So(test(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue) + So(test(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse) + }) +} diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index 19fd46db629..e220f42d67d 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -133,7 +133,7 @@ func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, erro condition.Reducer = NewSimpleReducer(reducerJson.Get("type").MustString()) evaluatorJson := model.Get("evaluator") - evaluator, err := NewDefaultAlertEvaluator(evaluatorJson) + evaluator, err := NewAlertEvaluator(evaluatorJson) if err != nil { return nil, err }