From ad56f67ad1706c240d580059830b880c33c896c9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 21 Oct 2016 14:00:20 +0200 Subject: [PATCH] feat(alerting): add support to keep last state on no data closes #6332 --- pkg/models/alert.go | 2 + pkg/services/alerting/result_handler.go | 12 ++- pkg/services/alerting/result_handler_test.go | 84 +++++++------------- public/app/features/alerting/alert_def.ts | 1 + 4 files changed, 41 insertions(+), 58 deletions(-) diff --git a/pkg/models/alert.go b/pkg/models/alert.go index f50bb3193dc..4bad6a33715 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -15,6 +15,8 @@ const ( AlertStatePaused AlertStateType = "paused" AlertStateAlerting AlertStateType = "alerting" AlertStateOK AlertStateType = "ok" + + KeepLastAlertState AlertStateType = "keep_last" ) func (s AlertStateType) IsValid() bool { diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 4372955803a..e27f8d9c35b 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -41,7 +41,6 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { evalContext.Rule.State = m.AlertStateAlerting annotationData = simplejson.NewFromAny(evalContext.EvalMatches) } else { - // handle no data case if evalContext.NoDataFound { evalContext.Rule.State = evalContext.Rule.NoDataState } else { @@ -50,7 +49,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { } countStateResult(evalContext.Rule.State) - if evalContext.Rule.State != oldState { + if handler.shouldUpdateAlertState(evalContext, oldState) { handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "oldState", oldState) cmd := &m.SetAlertStateCommand{ @@ -91,6 +90,15 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { return nil } +func (handler *DefaultResultHandler) shouldUpdateAlertState(evalContext *EvalContext, oldState m.AlertStateType) bool { + if evalContext.NoDataFound && evalContext.Rule.NoDataState == m.KeepLastAlertState { + evalContext.Rule.State = oldState + return false + } + + return evalContext.Rule.State != oldState +} + func countStateResult(state m.AlertStateType) { switch state { case m.AlertStateAlerting: diff --git a/pkg/services/alerting/result_handler_test.go b/pkg/services/alerting/result_handler_test.go index 32589bef172..cf56d402273 100644 --- a/pkg/services/alerting/result_handler_test.go +++ b/pkg/services/alerting/result_handler_test.go @@ -1,58 +1,30 @@ package alerting -// import ( -// "testing" -// "time" -// -// "github.com/grafana/grafana/pkg/bus" -// m "github.com/grafana/grafana/pkg/models" -// "github.com/grafana/grafana/pkg/services/alerting/alertstates" -// -// . "github.com/smartystreets/goconvey/convey" -// ) -// -// func TestAlertResultHandler(t *testing.T) { -// Convey("Test result Handler", t, func() { -// resultHandler := ResultHandlerImpl{} -// mockResult := &AlertResultContext{ -// Triggered: false, -// Rule: &AlertRule{ -// Id: 1, -// OrgId 1, -// }, -// } -// mockAlertState := &m.AlertState{} -// bus.ClearBusHandlers() -// bus.AddHandler("test", func(query *m.GetLastAlertStateQuery) error { -// query.Result = mockAlertState -// return nil -// }) -// -// Convey("Should update", func() { -// -// Convey("when no earlier alert state", func() { -// mockAlertState = nil -// So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue) -// }) -// -// Convey("alert state have changed", func() { -// mockAlertState = &m.AlertState{ -// State: alertstates.Critical, -// } -// mockResult.Triggered = false -// So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue) -// }) -// -// Convey("last alert state was 15min ago", func() { -// now := time.Now() -// mockAlertState = &m.AlertState{ -// State: alertstates.Critical, -// Created: now.Add(time.Minute * -30), -// } -// mockResult.Triggered = true -// mockResult.StartTime = time.Now() -// So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue) -// }) -// }) -// }) -// } +import ( + "context" + "testing" + + "github.com/grafana/grafana/pkg/models" + . "github.com/smartystreets/goconvey/convey" +) + +func TestAlertResultHandler(t *testing.T) { + Convey("Test result Handler", t, func() { + + handler := NewResultHandler() + evalContext := NewEvalContext(context.TODO(), &Rule{}) + + Convey("Should update", func() { + + Convey("when no earlier alert state", func() { + oldState := models.AlertStateOK + + evalContext.Rule.State = models.AlertStateAlerting + evalContext.Rule.NoDataState = models.KeepLastAlertState + evalContext.NoDataFound = true + + So(handler.shouldUpdateAlertState(evalContext, oldState), ShouldBeFalse) + }) + }) + }) +} diff --git a/public/app/features/alerting/alert_def.ts b/public/app/features/alerting/alert_def.ts index 8e9a86735ad..9c567d2ebc5 100644 --- a/public/app/features/alerting/alert_def.ts +++ b/public/app/features/alerting/alert_def.ts @@ -40,6 +40,7 @@ var noDataModes = [ {text: 'OK', value: 'ok'}, {text: 'Alerting', value: 'alerting'}, {text: 'No Data', value: 'no_data'}, + {text: 'Keep Last', value: 'keep_last'}, ]; function createReducerPart(model) {