Procházet zdrojové kódy

feat(alerting): add support for "no_value" evaluator

bergquist před 9 roky
rodič
revize
d28df71c3b

+ 60 - 27
pkg/services/alerting/conditions/evaluator.go

@@ -9,37 +9,86 @@ import (
 )
 )
 
 
 var (
 var (
-	defaultTypes []string = []string{"gt", "lt"}
-	rangedTypes  []string = []string{"within_range", "outside_range"}
+	defaultTypes   []string = []string{"gt", "lt"}
+	rangedTypes    []string = []string{"within_range", "outside_range"}
+	paramlessTypes []string = []string{"no_value"}
 )
 )
 
 
 type AlertEvaluator interface {
 type AlertEvaluator interface {
 	Eval(timeSeries *tsdb.TimeSeries, reducedValue float64) bool
 	Eval(timeSeries *tsdb.TimeSeries, reducedValue float64) bool
 }
 }
 
 
-type DefaultAlertEvaluator struct {
+type ParameterlessEvaluator struct {
+	Type string
+}
+
+func (e *ParameterlessEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
+	return len(series.Points) == 0
+}
+
+type ThresholdEvaluator struct {
 	Type      string
 	Type      string
 	Threshold float64
 	Threshold float64
 }
 }
 
 
-func (e *DefaultAlertEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
+func newThresholdEvaludator(typ string, model *simplejson.Json) (*ThresholdEvaluator, error) {
+	params := model.Get("params").MustArray()
+	if len(params) == 0 {
+		return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
+	}
+
+	firstParam, ok := params[0].(json.Number)
+	if !ok {
+		return nil, alerting.ValidationError{Reason: "Evaluator has invalid parameter"}
+	}
+
+	defaultEval := &ThresholdEvaluator{Type: typ}
+	defaultEval.Threshold, _ = firstParam.Float64()
+	return defaultEval, nil
+}
+
+func (e *ThresholdEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
 	switch e.Type {
 	switch e.Type {
 	case "gt":
 	case "gt":
 		return reducedValue > e.Threshold
 		return reducedValue > e.Threshold
 	case "lt":
 	case "lt":
 		return reducedValue < e.Threshold
 		return reducedValue < e.Threshold
+	case "no_value":
+		return len(series.Points) == 0
 	}
 	}
 
 
 	return false
 	return false
 }
 }
 
 
-type RangedAlertEvaluator struct {
+type RangedEvaluator struct {
 	Type  string
 	Type  string
 	Lower float64
 	Lower float64
 	Upper float64
 	Upper float64
 }
 }
 
 
-func (e *RangedAlertEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
+func newRangedEvaluator(typ string, model *simplejson.Json) (*RangedEvaluator, error) {
+	params := model.Get("params").MustArray()
+	if len(params) == 0 {
+		return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
+	}
+
+	firstParam, ok := params[0].(json.Number)
+	if !ok {
+		return nil, alerting.ValidationError{Reason: "Evaluator has invalid parameter"}
+	}
+
+	secondParam, ok := params[1].(json.Number)
+	if !ok {
+		return nil, alerting.ValidationError{Reason: "Evaluator has invalid second parameter"}
+	}
+
+	rangedEval := &RangedEvaluator{Type: typ}
+	rangedEval.Lower, _ = firstParam.Float64()
+	rangedEval.Upper, _ = secondParam.Float64()
+	return rangedEval, nil
+}
+
+func (e *RangedEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
 	switch e.Type {
 	switch e.Type {
 	case "within_range":
 	case "within_range":
 		return (e.Lower < reducedValue && e.Upper > reducedValue) || (e.Upper < reducedValue && e.Lower > reducedValue)
 		return (e.Lower < reducedValue && e.Upper > reducedValue) || (e.Upper < reducedValue && e.Lower > reducedValue)
@@ -56,32 +105,16 @@ func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) {
 		return nil, alerting.ValidationError{Reason: "Evaluator missing type property"}
 		return nil, alerting.ValidationError{Reason: "Evaluator missing type property"}
 	}
 	}
 
 
-	params := model.Get("params").MustArray()
-	if len(params) == 0 {
-		return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
-	}
-
-	firstParam, ok := params[0].(json.Number)
-	if !ok {
-		return nil, alerting.ValidationError{Reason: "Evaluator has invalid parameter"}
-	}
-
 	if inSlice(typ, defaultTypes) {
 	if inSlice(typ, defaultTypes) {
-		defaultEval := &DefaultAlertEvaluator{Type: typ}
-		defaultEval.Threshold, _ = firstParam.Float64()
-		return defaultEval, nil
+		return newThresholdEvaludator(typ, model)
 	}
 	}
 
 
 	if inSlice(typ, rangedTypes) {
 	if inSlice(typ, rangedTypes) {
-		secondParam, ok := params[1].(json.Number)
-		if !ok {
-			return nil, alerting.ValidationError{Reason: "Evaluator has invalid second parameter"}
-		}
+		return newRangedEvaluator(typ, model)
+	}
 
 
-		rangedEval := &RangedAlertEvaluator{Type: typ}
-		rangedEval.Lower, _ = firstParam.Float64()
-		rangedEval.Upper, _ = secondParam.Float64()
-		return rangedEval, nil
+	if inSlice(typ, paramlessTypes) {
+		return &ParameterlessEvaluator{Type: typ}, nil
 	}
 	}
 
 
 	return nil, alerting.ValidationError{Reason: "Evaludator invalid evaluator type"}
 	return nil, alerting.ValidationError{Reason: "Evaludator invalid evaluator type"}

+ 4 - 0
pkg/services/alerting/conditions/evaluator_test.go

@@ -42,4 +42,8 @@ func TestEvalutors(t *testing.T) {
 		So(test(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue)
 		So(test(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue)
 		So(test(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse)
 		So(test(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse)
 	})
 	})
+
+	Convey("no_value", t, func() {
+		So(test(`{"type": "no_value", "params": [] }`, 1000), ShouldBeTrue)
+	})
 }
 }

+ 1 - 1
pkg/services/alerting/conditions/query_test.go

@@ -34,7 +34,7 @@ func TestQueryCondition(t *testing.T) {
 				})
 				})
 
 
 				Convey("Can read evaluator", func() {
 				Convey("Can read evaluator", func() {
-					evaluator, ok := ctx.condition.Evaluator.(*DefaultAlertEvaluator)
+					evaluator, ok := ctx.condition.Evaluator.(*ThresholdEvaluator)
 					So(ok, ShouldBeTrue)
 					So(ok, ShouldBeTrue)
 					So(evaluator.Type, ShouldEqual, "gt")
 					So(evaluator.Type, ShouldEqual, "gt")
 				})
 				})