Prechádzať zdrojové kódy

feat(alerting): starting work on condition evaluation

Torkel Ödegaard 9 rokov pred
rodič
commit
3219d98a92

+ 40 - 0
pkg/services/alerting/conditions_test.go

@@ -0,0 +1,40 @@
+package alerting
+
+import (
+	"testing"
+
+	"github.com/grafana/grafana/pkg/components/simplejson"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestQueryCondition(t *testing.T) {
+
+	Convey("when evaluating query condition", t, func() {
+
+		Convey("Given avg() and > 100", func() {
+
+			jsonModel, err := simplejson.NewJson([]byte(`{
+            "type": "query",
+            "query":  {
+              "params": ["A", "5m", "now"],
+              "datasourceId": 1,
+              "model": {"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
+            },
+            "reducer": {"type": "avg", "params": []},
+            "evaluator": {"type": ">", "params": [100]}
+          }`))
+			So(err, ShouldBeNil)
+
+			condition, err := NewQueryCondition(jsonModel)
+			So(err, ShouldBeNil)
+
+			Convey("Should set result to triggered when avg is above 100", func() {
+				context := &AlertResultContext{}
+				condition.Eval(context)
+
+				So(context.Triggered, ShouldBeTrue)
+			})
+		})
+
+	})
+}

+ 1 - 1
pkg/services/alerting/engine.go

@@ -14,7 +14,7 @@ type Engine struct {
 	clock           clock.Clock
 	clock           clock.Clock
 	ticker          *Ticker
 	ticker          *Ticker
 	scheduler       Scheduler
 	scheduler       Scheduler
-	handler         AlertingHandler
+	handler         AlertHandler
 	ruleReader      RuleReader
 	ruleReader      RuleReader
 	log             log.Logger
 	log             log.Logger
 	responseHandler ResultHandler
 	responseHandler ResultHandler

+ 1 - 0
pkg/services/alerting/extractor.go

@@ -90,6 +90,7 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
 				Handler:     jsonAlert.Get("handler").MustInt64(),
 				Handler:     jsonAlert.Get("handler").MustInt64(),
 				Enabled:     jsonAlert.Get("enabled").MustBool(),
 				Enabled:     jsonAlert.Get("enabled").MustBool(),
 				Description: jsonAlert.Get("description").MustString(),
 				Description: jsonAlert.Get("description").MustString(),
+				Severity:    jsonAlert.Get("severity").MustString(),
 				Frequency:   getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString()),
 				Frequency:   getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString()),
 			}
 			}
 
 

+ 10 - 0
pkg/services/alerting/handler.go

@@ -31,6 +31,16 @@ func (e *HandlerImpl) eval(rule *AlertRule) *AlertResultContext {
 
 
 	for _, condition := range rule.Conditions {
 	for _, condition := range rule.Conditions {
 		condition.Eval(result)
 		condition.Eval(result)
+
+		// break if condition could not be evaluated
+		if result.Error != nil {
+			break
+		}
+
+		// break if result has not triggered yet
+		if result.Triggered == false {
+			break
+		}
 	}
 	}
 
 
 	result.EndTime = time.Now()
 	result.EndTime = time.Now()

+ 145 - 154
pkg/services/alerting/handler_test.go

@@ -3,171 +3,162 @@ package alerting
 import (
 import (
 	"testing"
 	"testing"
 
 
-	"github.com/grafana/grafana/pkg/components/simplejson"
-	"github.com/grafana/grafana/pkg/models"
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
 )
 )
 
 
+type conditionStub struct {
+	triggered bool
+}
+
+func (c *conditionStub) Eval(context *AlertResultContext) {
+	context.Triggered = c.triggered
+}
+
 func TestAlertingExecutor(t *testing.T) {
 func TestAlertingExecutor(t *testing.T) {
 	Convey("Test alert execution", t, func() {
 	Convey("Test alert execution", t, func() {
 		handler := NewHandler()
 		handler := NewHandler()
 
 
-		Convey("single time serie", func() {
-			Convey("Show return ok since avg is above 2", func() {
-				json := `
-        {
-				"name": "name2",
-				"description": "desc2",
-				"handler": 0,
-				"enabled": true,
-				"frequency": "60s",
-        "conditions": [
-          {
-            "type": "query",
-            "query":  {
-              "params": ["A", "5m", "now"],
-              "datasourceId": 1,
-              "model": {"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
-            },
-            "reducer": {"type": "avg", "params": []},
-            "evaluator": {"type": ">", "params": [100]}
-          }
-        ]
+		Convey("Show return triggered with single passing condition", func() {
+			rule := &AlertRule{
+				Conditions: []AlertCondition{&conditionStub{
+					triggered: true,
+				}},
 			}
 			}
-			`
-
-				alertJSON, jsonErr := simplejson.NewJson([]byte(json))
-				So(jsonErr, ShouldBeNil)
 
 
-				alert := &models.Alert{Settings: alertJSON}
-				rule, _ := NewAlertRuleFromDBModel(alert)
-
-				// timeSeries := []*tsdb.TimeSeries{
-				// 	tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
-				// }
+			result := handler.eval(rule)
+			So(result.Triggered, ShouldEqual, true)
+		})
 
 
-				result := handler.eval(rule)
-				So(result.Triggered, ShouldEqual, true)
-			})
+		Convey("Show return false with not passing condition", func() {
+			rule := &AlertRule{
+				Conditions: []AlertCondition{
+					&conditionStub{triggered: true},
+					&conditionStub{triggered: false},
+				},
+			}
 
 
-			// 	Convey("Show return critical since below 2", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: "<"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Critical)
-			// 	})
-			//
-			// 	Convey("Show return critical since sum is above 10", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("sum"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{9, 0}, {9, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Critical)
-			// 	})
-			//
-			// 	Convey("Show return ok since avg is below 10", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{9, 0}, {9, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Ok)
-			// 	})
-			//
-			// 	Convey("Show return ok since min is below 10", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}, {9, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Ok)
-			// 	})
-			//
-			// 	Convey("Show return ok since max is above 10", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("max"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{6, 0}, {11, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Critical)
-			// 	})
-			//
-			// })
-			//
-			// Convey("muliple time series", func() {
-			// 	Convey("both are ok", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Ok)
-			// 	})
-			//
-			// 	Convey("first serie is good, second is critical", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Critical)
-			// 	})
-			//
-			// 	Convey("first serie is warn, second is critical", func() {
-			// 		rule := &AlertRule{
-			// 			Critical:    Level{Value: 10, Operator: ">"},
-			// 			Warning:     Level{Value: 5, Operator: ">"},
-			// 			Transformer: transformers.NewAggregationTransformer("avg"),
-			// 		}
-			//
-			// 		timeSeries := []*tsdb.TimeSeries{
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{6, 0}}),
-			// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}}),
-			// 		}
-			//
-			// 		result := executor.evaluateRule(rule, timeSeries)
-			// 		So(result.State, ShouldEqual, alertstates.Critical)
-			// 	})
+			result := handler.eval(rule)
+			So(result.Triggered, ShouldEqual, false)
 		})
 		})
+
+		// 	Convey("Show return critical since below 2", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: "<"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Critical)
+		// 	})
+		//
+		// 	Convey("Show return critical since sum is above 10", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("sum"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{9, 0}, {9, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Critical)
+		// 	})
+		//
+		// 	Convey("Show return ok since avg is below 10", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{9, 0}, {9, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Ok)
+		// 	})
+		//
+		// 	Convey("Show return ok since min is below 10", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}, {9, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Ok)
+		// 	})
+		//
+		// 	Convey("Show return ok since max is above 10", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("max"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{6, 0}, {11, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Critical)
+		// 	})
+		//
+		// })
+		//
+		// Convey("muliple time series", func() {
+		// 	Convey("both are ok", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Ok)
+		// 	})
+		//
+		// 	Convey("first serie is good, second is critical", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{2, 0}}),
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Critical)
+		// 	})
+		//
+		// 	Convey("first serie is warn, second is critical", func() {
+		// 		rule := &AlertRule{
+		// 			Critical:    Level{Value: 10, Operator: ">"},
+		// 			Warning:     Level{Value: 5, Operator: ">"},
+		// 			Transformer: transformers.NewAggregationTransformer("avg"),
+		// 		}
+		//
+		// 		timeSeries := []*tsdb.TimeSeries{
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{6, 0}}),
+		// 			tsdb.NewTimeSeries("test1", [][2]float64{{11, 0}}),
+		// 		}
+		//
+		// 		result := executor.evaluateRule(rule, timeSeries)
+		// 		So(result.State, ShouldEqual, alertstates.Critical)
+		// 	})
+		// })
 	})
 	})
 }
 }

+ 1 - 1
pkg/services/alerting/interfaces.go

@@ -2,7 +2,7 @@ package alerting
 
 
 import "time"
 import "time"
 
 
-type AlertingHandler interface {
+type AlertHandler interface {
 	Execute(rule *AlertRule, resultChan chan *AlertResultContext)
 	Execute(rule *AlertRule, resultChan chan *AlertResultContext)
 }
 }