Commit 8bb2f115 by Torkel Ödegaard

Merge branch 'alerting_keeplast'

parents 4999fff2 a2e14f56
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
type AlertStateType string type AlertStateType string
type AlertSeverityType string type AlertSeverityType string
type NoDataOption string
const ( const (
AlertStateNoData AlertStateType = "no_data" AlertStateNoData AlertStateType = "no_data"
...@@ -17,10 +18,25 @@ const ( ...@@ -17,10 +18,25 @@ const (
AlertStateOK AlertStateType = "ok" AlertStateOK AlertStateType = "ok"
) )
const (
NoDataSetNoData NoDataOption = "no_data"
NoDataSetAlerting NoDataOption = "alerting"
NoDataSetOK NoDataOption = "ok"
NoDataKeepState NoDataOption = "keep_state"
)
func (s AlertStateType) IsValid() bool { func (s AlertStateType) IsValid() bool {
return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused
} }
func (s NoDataOption) IsValid() bool {
return s == NoDataSetNoData || s == NoDataSetAlerting || s == NoDataSetOK || s == NoDataKeepState
}
func (s NoDataOption) ToAlertState() AlertStateType {
return AlertStateType(s)
}
type Alert struct { type Alert struct {
Id int64 Id int64
Version int64 Version int64
......
...@@ -30,34 +30,35 @@ func NewResultHandler() *DefaultResultHandler { ...@@ -30,34 +30,35 @@ func NewResultHandler() *DefaultResultHandler {
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
oldState := evalContext.Rule.State oldState := evalContext.Rule.State
exeuctionError := "" executionError := ""
annotationData := simplejson.New() annotationData := simplejson.New()
if evalContext.Error != nil { if evalContext.Error != nil {
handler.log.Error("Alert Rule Result Error", "ruleId", evalContext.Rule.Id, "error", evalContext.Error) handler.log.Error("Alert Rule Result Error", "ruleId", evalContext.Rule.Id, "error", evalContext.Error)
evalContext.Rule.State = m.AlertStateExecError evalContext.Rule.State = m.AlertStateExecError
exeuctionError = evalContext.Error.Error() executionError = evalContext.Error.Error()
annotationData.Set("errorMessage", exeuctionError) annotationData.Set("errorMessage", executionError)
} else if evalContext.Firing { } else if evalContext.Firing {
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 if evalContext.Rule.NoDataState != m.NoDataKeepState {
evalContext.Rule.State = evalContext.Rule.NoDataState.ToAlertState()
}
} else { } else {
evalContext.Rule.State = m.AlertStateOK evalContext.Rule.State = m.AlertStateOK
} }
} }
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{
AlertId: evalContext.Rule.Id, AlertId: evalContext.Rule.Id,
OrgId: evalContext.Rule.OrgId, OrgId: evalContext.Rule.OrgId,
State: evalContext.Rule.State, State: evalContext.Rule.State,
Error: exeuctionError, Error: executionError,
EvalData: annotationData, EvalData: annotationData,
} }
...@@ -91,6 +92,10 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { ...@@ -91,6 +92,10 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
return nil return nil
} }
func (handler *DefaultResultHandler) shouldUpdateAlertState(evalContext *EvalContext, oldState m.AlertStateType) bool {
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 (
// "context"
// "testing" // "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/grafana/grafana/pkg/models"
// . "github.com/smartystreets/goconvey/convey" // . "github.com/smartystreets/goconvey/convey"
// ) // )
// //
// func TestAlertResultHandler(t *testing.T) { // func TestAlertResultHandler(t *testing.T) {
// Convey("Test result Handler", t, func() { // Convey("Test result Handler", t, func() {
// resultHandler := ResultHandlerImpl{} //
// mockResult := &AlertResultContext{ // handler := NewResultHandler()
// Triggered: false, // evalContext := NewEvalContext(context.TODO(), &Rule{})
// 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("Should update", func() {
// //
// Convey("when no earlier alert state", func() { // Convey("when no earlier alert state", func() {
// mockAlertState = nil // oldState := models.AlertStateOK
// So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue)
// })
// //
// Convey("alert state have changed", func() { // evalContext.Rule.State = models.AlertStateAlerting
// mockAlertState = &m.AlertState{ // evalContext.Rule.NoDataState = models.NoDataKeepState
// State: alertstates.Critical, // evalContext.NoDataFound = true
// }
// mockResult.Triggered = false
// So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue)
// })
// //
// Convey("last alert state was 15min ago", func() { // So(handler.shouldUpdateAlertState(evalContext, oldState), ShouldBeFalse)
// 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)
// }) // })
// }) // })
// }) // })
......
...@@ -18,7 +18,7 @@ type Rule struct { ...@@ -18,7 +18,7 @@ type Rule struct {
Frequency int64 Frequency int64
Name string Name string
Message string Message string
NoDataState m.AlertStateType NoDataState m.NoDataOption
State m.AlertStateType State m.AlertStateType
Conditions []Condition Conditions []Condition
Notifications []int64 Notifications []int64
...@@ -76,7 +76,7 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { ...@@ -76,7 +76,7 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) {
model.Message = ruleDef.Message model.Message = ruleDef.Message
model.Frequency = ruleDef.Frequency model.Frequency = ruleDef.Frequency
model.State = ruleDef.State model.State = ruleDef.State
model.NoDataState = m.AlertStateType(ruleDef.Settings.Get("noDataState").MustString("no_data")) model.NoDataState = m.NoDataOption(ruleDef.Settings.Get("noDataState").MustString("no_data"))
for _, v := range ruleDef.Settings.Get("notifications").MustArray() { for _, v := range ruleDef.Settings.Get("notifications").MustArray() {
jsonModel := simplejson.NewFromAny(v) jsonModel := simplejson.NewFromAny(v)
......
...@@ -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