diff --git a/pkg/services/alerting/eval_handler.go b/pkg/services/alerting/eval_handler.go index 075d0833fdf..20c6b952465 100644 --- a/pkg/services/alerting/eval_handler.go +++ b/pkg/services/alerting/eval_handler.go @@ -23,6 +23,7 @@ func NewEvalHandler() *DefaultEvalHandler { func (e *DefaultEvalHandler) Eval(context *EvalContext) { firing := true + noDataFound := true conditionEvals := "" for i := 0; i < len(context.Rule.Conditions); i++ { @@ -40,8 +41,10 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) { // calculating Firing based on operator if cr.Operator == "or" { firing = firing || cr.Firing + noDataFound = noDataFound || cr.NoDataFound } else { firing = firing && cr.Firing + noDataFound = noDataFound && cr.NoDataFound } if i > 0 { @@ -55,6 +58,7 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) { context.ConditionEvals = conditionEvals + " = " + strconv.FormatBool(firing) context.Firing = firing + context.NoDataFound = noDataFound context.EndTime = time.Now() elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond metrics.M_Alerting_Exeuction_Time.Update(elapsedTime) diff --git a/pkg/services/alerting/eval_handler_test.go b/pkg/services/alerting/eval_handler_test.go index 84905870f52..73d675e3fa7 100644 --- a/pkg/services/alerting/eval_handler_test.go +++ b/pkg/services/alerting/eval_handler_test.go @@ -11,10 +11,11 @@ type conditionStub struct { firing bool operator string matches []*EvalMatch + noData bool } func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) { - return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator}, nil + return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil } func TestAlertingExecutor(t *testing.T) { @@ -127,5 +128,41 @@ func TestAlertingExecutor(t *testing.T) { So(context.Firing, ShouldEqual, true) So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true") }) + + Convey("Should return no data if one condition has nodata", func() { + context := NewEvalContext(context.TODO(), &Rule{ + Conditions: []Condition{ + &conditionStub{operator: "and", noData: true}, + }, + }) + + handler.Eval(context) + So(context.Firing, ShouldEqual, false) + So(context.NoDataFound, ShouldBeTrue) + }) + + Convey("Should return no data if both conditions have no data and using AND", func() { + context := NewEvalContext(context.TODO(), &Rule{ + Conditions: []Condition{ + &conditionStub{operator: "and", noData: true}, + &conditionStub{operator: "and", noData: false}, + }, + }) + + handler.Eval(context) + So(context.NoDataFound, ShouldBeFalse) + }) + + Convey("Should not return no data if both conditions have no data and using OR", func() { + context := NewEvalContext(context.TODO(), &Rule{ + Conditions: []Condition{ + &conditionStub{operator: "or", noData: true}, + &conditionStub{operator: "or", noData: false}, + }, + }) + + handler.Eval(context) + So(context.NoDataFound, ShouldBeTrue) + }) }) }