Commit ad56f67a by bergquist

feat(alerting): add support to keep last state on no data

closes #6332
parent 96008c97
...@@ -15,6 +15,8 @@ const ( ...@@ -15,6 +15,8 @@ const (
AlertStatePaused AlertStateType = "paused" AlertStatePaused AlertStateType = "paused"
AlertStateAlerting AlertStateType = "alerting" AlertStateAlerting AlertStateType = "alerting"
AlertStateOK AlertStateType = "ok" AlertStateOK AlertStateType = "ok"
KeepLastAlertState AlertStateType = "keep_last"
) )
func (s AlertStateType) IsValid() bool { func (s AlertStateType) IsValid() bool {
......
...@@ -41,7 +41,6 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { ...@@ -41,7 +41,6 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
evalContext.Rule.State = m.AlertStateAlerting evalContext.Rule.State = m.AlertStateAlerting
annotationData = simplejson.NewFromAny(evalContext.EvalMatches) annotationData = simplejson.NewFromAny(evalContext.EvalMatches)
} else { } else {
// handle no data case
if evalContext.NoDataFound { if evalContext.NoDataFound {
evalContext.Rule.State = evalContext.Rule.NoDataState evalContext.Rule.State = evalContext.Rule.NoDataState
} else { } else {
...@@ -50,7 +49,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { ...@@ -50,7 +49,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
} }
countStateResult(evalContext.Rule.State) 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) handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "oldState", oldState)
cmd := &m.SetAlertStateCommand{ cmd := &m.SetAlertStateCommand{
...@@ -91,6 +90,15 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { ...@@ -91,6 +90,15 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
return nil 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) { func countStateResult(state m.AlertStateType) {
switch state { switch state {
case m.AlertStateAlerting: case m.AlertStateAlerting:
......
package alerting package alerting
// import ( import (
// "testing" "context"
// "time" "testing"
//
// "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/models"
// m "github.com/grafana/grafana/pkg/models" . "github.com/smartystreets/goconvey/convey"
// "github.com/grafana/grafana/pkg/services/alerting/alertstates" )
//
// . "github.com/smartystreets/goconvey/convey" func TestAlertResultHandler(t *testing.T) {
// ) Convey("Test result Handler", t, func() {
//
// func TestAlertResultHandler(t *testing.T) { handler := NewResultHandler()
// Convey("Test result Handler", t, func() { evalContext := NewEvalContext(context.TODO(), &Rule{})
// resultHandler := ResultHandlerImpl{}
// mockResult := &AlertResultContext{ Convey("Should update", func() {
// Triggered: false,
// Rule: &AlertRule{ Convey("when no earlier alert state", func() {
// Id: 1, oldState := models.AlertStateOK
// OrgId 1,
// }, evalContext.Rule.State = models.AlertStateAlerting
// } evalContext.Rule.NoDataState = models.KeepLastAlertState
// mockAlertState := &m.AlertState{} evalContext.NoDataFound = true
// bus.ClearBusHandlers()
// bus.AddHandler("test", func(query *m.GetLastAlertStateQuery) error { So(handler.shouldUpdateAlertState(evalContext, oldState), ShouldBeFalse)
// 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)
// })
// })
// })
// }
...@@ -40,6 +40,7 @@ var noDataModes = [ ...@@ -40,6 +40,7 @@ var noDataModes = [
{text: 'OK', value: 'ok'}, {text: 'OK', value: 'ok'},
{text: 'Alerting', value: 'alerting'}, {text: 'Alerting', value: 'alerting'},
{text: 'No Data', value: 'no_data'}, {text: 'No Data', value: 'no_data'},
{text: 'Keep Last', value: 'keep_last'},
]; ];
function createReducerPart(model) { function createReducerPart(model) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment