feat(alerting): move response handling to seperate file

This commit is contained in:
bergquist 2016-06-23 12:57:10 +02:00
parent 67197d54f9
commit f95be63c43
3 changed files with 147 additions and 60 deletions

View File

@ -5,35 +5,32 @@ import (
"time" "time"
"github.com/benbjohnson/clock" "github.com/benbjohnson/clock"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting/alertstates" "github.com/grafana/grafana/pkg/services/alerting/alertstates"
) )
type Engine struct { type Engine struct {
execQueue chan *AlertJob execQueue chan *AlertJob
resultQueue chan *AlertResult resultQueue chan *AlertResult
clock clock.Clock clock clock.Clock
ticker *Ticker ticker *Ticker
scheduler Scheduler scheduler Scheduler
handler AlertingHandler handler AlertingHandler
ruleReader RuleReader ruleReader RuleReader
log log.Logger log log.Logger
notifier Notifier responseHandler ResultHandler
} }
func NewEngine() *Engine { func NewEngine() *Engine {
e := &Engine{ e := &Engine{
ticker: NewTicker(time.Now(), time.Second*0, clock.New()), ticker: NewTicker(time.Now(), time.Second*0, clock.New()),
execQueue: make(chan *AlertJob, 1000), execQueue: make(chan *AlertJob, 1000),
resultQueue: make(chan *AlertResult, 1000), resultQueue: make(chan *AlertResult, 1000),
scheduler: NewScheduler(), scheduler: NewScheduler(),
handler: NewHandler(), handler: NewHandler(),
ruleReader: NewRuleReader(), ruleReader: NewRuleReader(),
log: log.New("alerting.engine"), log: log.New("alerting.engine"),
notifier: NewNotifier(), responseHandler: NewResultHandler(),
} }
return e return e
@ -134,49 +131,13 @@ func (e *Engine) resultHandler() {
result.State = alertstates.Critical result.State = alertstates.Critical
result.Description = fmt.Sprintf("Failed to run check after %d retires, Error: %v", maxAlertExecutionRetries, result.Error) result.Description = fmt.Sprintf("Failed to run check after %d retires, Error: %v", maxAlertExecutionRetries, result.Error)
e.reactToState(result) //e.reactToState(result)
e.responseHandler.Handle(result)
} }
} else { } else {
result.AlertJob.ResetRetry() result.AlertJob.ResetRetry()
e.reactToState(result) //e.reactToState(result)
e.responseHandler.Handle(result)
} }
} }
} }
func (e *Engine) reactToState(result *AlertResult) {
if shouldUpdateState(result) {
cmd := &m.UpdateAlertStateCommand{
AlertId: result.AlertJob.Rule.Id,
NewState: result.State,
Info: result.Description,
OrgId: result.AlertJob.Rule.OrgId,
TriggeredAlerts: simplejson.NewFromAny(result.TriggeredAlerts),
}
if err := bus.Dispatch(cmd); err != nil {
e.log.Error("Failed to save state", "error", err)
}
e.log.Debug("will notify about new state", "new state", result.State)
e.notifier.Notify(result)
}
}
func shouldUpdateState(result *AlertResult) bool {
query := &m.GetLastAlertStateQuery{
AlertId: result.AlertJob.Rule.Id,
OrgId: result.AlertJob.Rule.OrgId,
}
if err := bus.Dispatch(query); err != nil {
log.Error2("Failed to read last alert state", "error", err)
return false
}
now := time.Now()
noEarlierState := query.Result == nil
olderThen15Min := query.Result.Created.Before(now.Add(time.Minute * -15))
changedState := query.Result.NewState != result.State
return noEarlierState || changedState || olderThen15Min
}

View File

@ -0,0 +1,68 @@
package alerting
import (
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log"
m "github.com/grafana/grafana/pkg/models"
)
type ResultHandler interface {
Handle(result *AlertResult)
}
type ResultHandlerImpl struct {
notifier Notifier
log log.Logger
}
func NewResultHandler() *ResultHandlerImpl {
return &ResultHandlerImpl{
log: log.New("alerting.responseHandler"),
notifier: NewNotifier(),
}
}
func (handler *ResultHandlerImpl) Handle(result *AlertResult) {
if handler.shouldUpdateState(result) {
cmd := &m.UpdateAlertStateCommand{
AlertId: result.AlertJob.Rule.Id,
NewState: result.State,
Info: result.Description,
OrgId: result.AlertJob.Rule.OrgId,
TriggeredAlerts: simplejson.NewFromAny(result.TriggeredAlerts),
}
if err := bus.Dispatch(cmd); err != nil {
handler.log.Error("Failed to save state", "error", err)
}
handler.log.Debug("will notify about new state", "new state", result.State)
handler.notifier.Notify(result)
}
}
func (handler *ResultHandlerImpl) shouldUpdateState(result *AlertResult) bool {
query := &m.GetLastAlertStateQuery{
AlertId: result.AlertJob.Rule.Id,
OrgId: result.AlertJob.Rule.OrgId,
}
if err := bus.Dispatch(query); err != nil {
log.Error2("Failed to read last alert state", "error", err)
return false
}
now := time.Now()
if query.Result == nil {
return true
}
olderThen15Min := query.Result.Created.Before(now.Add(time.Minute * -15))
changedState := query.Result.NewState != result.State
return changedState || olderThen15Min
}

View File

@ -0,0 +1,58 @@
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 := &AlertResult{
State: alertstates.Ok,
AlertJob: &AlertJob{
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{
NewState: alertstates.Critical,
}
mockResult.State = alertstates.Ok
So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue)
})
Convey("last alert state was 15min ago", func() {
now := time.Now()
mockAlertState = &m.AlertState{
NewState: alertstates.Critical,
Created: now.Add(time.Minute * -30),
}
mockResult.State = alertstates.Critical
So(resultHandler.shouldUpdateState(mockResult), ShouldBeTrue)
})
})
})
}