mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
parent
bccdd7cad1
commit
0873d493c2
@ -120,10 +120,10 @@ func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := backendCmd.Result
|
res := backendCmd.Result
|
||||||
|
|
||||||
dtoRes := &dtos.AlertTestResult{
|
dtoRes := &dtos.AlertTestResult{
|
||||||
Firing: res.Firing,
|
Firing: res.Firing,
|
||||||
ConditionEvals: res.ConditionEvals,
|
ConditionEvals: res.ConditionEvals,
|
||||||
|
State: res.Rule.State,
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
|
@ -36,6 +36,7 @@ type AlertTestCommand struct {
|
|||||||
|
|
||||||
type AlertTestResult struct {
|
type AlertTestResult struct {
|
||||||
Firing bool `json:"firing"`
|
Firing bool `json:"firing"`
|
||||||
|
State m.AlertStateType `json:"state"`
|
||||||
ConditionEvals string `json:"conditionEvals"`
|
ConditionEvals string `json:"conditionEvals"`
|
||||||
TimeMs string `json:"timeMs"`
|
TimeMs string `json:"timeMs"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
"github.com/grafana/grafana/pkg/metrics"
|
"github.com/grafana/grafana/pkg/metrics"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DefaultEvalHandler struct {
|
type DefaultEvalHandler struct {
|
||||||
@ -60,6 +61,40 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) {
|
|||||||
context.Firing = firing
|
context.Firing = firing
|
||||||
context.NoDataFound = noDataFound
|
context.NoDataFound = noDataFound
|
||||||
context.EndTime = time.Now()
|
context.EndTime = time.Now()
|
||||||
|
context.Rule.State = e.getNewState(context)
|
||||||
|
|
||||||
elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond
|
elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond
|
||||||
metrics.M_Alerting_Execution_Time.Update(elapsedTime)
|
metrics.M_Alerting_Execution_Time.Update(elapsedTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should be move into evalContext once its been refactored.
|
||||||
|
func (handler *DefaultEvalHandler) getNewState(evalContext *EvalContext) models.AlertStateType {
|
||||||
|
if evalContext.Error != nil {
|
||||||
|
handler.log.Error("Alert Rule Result Error",
|
||||||
|
"ruleId", evalContext.Rule.Id,
|
||||||
|
"name", evalContext.Rule.Name,
|
||||||
|
"error", evalContext.Error,
|
||||||
|
"changing state to", evalContext.Rule.ExecutionErrorState.ToAlertState())
|
||||||
|
|
||||||
|
if evalContext.Rule.ExecutionErrorState == models.ExecutionErrorKeepState {
|
||||||
|
return evalContext.PrevAlertState
|
||||||
|
} else {
|
||||||
|
return evalContext.Rule.ExecutionErrorState.ToAlertState()
|
||||||
|
}
|
||||||
|
} else if evalContext.Firing {
|
||||||
|
return models.AlertStateAlerting
|
||||||
|
} else if evalContext.NoDataFound {
|
||||||
|
handler.log.Info("Alert Rule returned no data",
|
||||||
|
"ruleId", evalContext.Rule.Id,
|
||||||
|
"name", evalContext.Rule.Name,
|
||||||
|
"changing state to", evalContext.Rule.NoDataState.ToAlertState())
|
||||||
|
|
||||||
|
if evalContext.Rule.NoDataState == models.NoDataKeepState {
|
||||||
|
return evalContext.PrevAlertState
|
||||||
|
} else {
|
||||||
|
return evalContext.Rule.NoDataState.ToAlertState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.AlertStateOK
|
||||||
|
}
|
||||||
|
@ -2,8 +2,10 @@ package alerting
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,8 +20,8 @@ func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) {
|
|||||||
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil
|
return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlertingExecutor(t *testing.T) {
|
func TestAlertingEvaluationHandler(t *testing.T) {
|
||||||
Convey("Test alert execution", t, func() {
|
Convey("Test alert evaluation handler", t, func() {
|
||||||
handler := NewEvalHandler()
|
handler := NewEvalHandler()
|
||||||
|
|
||||||
Convey("Show return triggered with single passing condition", func() {
|
Convey("Show return triggered with single passing condition", func() {
|
||||||
@ -164,5 +166,73 @@ func TestAlertingExecutor(t *testing.T) {
|
|||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
So(context.NoDataFound, ShouldBeTrue)
|
So(context.NoDataFound, ShouldBeTrue)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("EvalHandler can replace alert state based for errors and no_data", func() {
|
||||||
|
ctx := NewEvalContext(context.TODO(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}})
|
||||||
|
dummieError := fmt.Errorf("dummie error")
|
||||||
|
Convey("Should update alert state", func() {
|
||||||
|
|
||||||
|
Convey("ok -> alerting", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
|
ctx.Firing = true
|
||||||
|
|
||||||
|
So(handler.getNewState(ctx), ShouldEqual, models.AlertStateAlerting)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("ok -> error(alerting)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
|
ctx.Error = dummieError
|
||||||
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorSetAlerting
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("ok -> error(keep_last)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
|
ctx.Error = dummieError
|
||||||
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("pending -> error(keep_last)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStatePending
|
||||||
|
ctx.Error = dummieError
|
||||||
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("ok -> no_data(alerting)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
|
ctx.Rule.NoDataState = models.NoDataSetAlerting
|
||||||
|
ctx.NoDataFound = true
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("ok -> no_data(keep_last)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
|
ctx.Rule.NoDataState = models.NoDataKeepState
|
||||||
|
ctx.NoDataFound = true
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("pending -> no_data(keep_last)", func() {
|
||||||
|
ctx.PrevAlertState = models.AlertStatePending
|
||||||
|
ctx.Rule.NoDataState = models.NoDataKeepState
|
||||||
|
ctx.NoDataFound = true
|
||||||
|
|
||||||
|
ctx.Rule.State = handler.getNewState(ctx)
|
||||||
|
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -27,43 +27,10 @@ func NewResultHandler() *DefaultResultHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *DefaultResultHandler) GetStateFromEvaluation(evalContext *EvalContext) m.AlertStateType {
|
|
||||||
if evalContext.Error != nil {
|
|
||||||
handler.log.Error("Alert Rule Result Error",
|
|
||||||
"ruleId", evalContext.Rule.Id,
|
|
||||||
"name", evalContext.Rule.Name,
|
|
||||||
"error", evalContext.Error,
|
|
||||||
"changing state to", evalContext.Rule.ExecutionErrorState.ToAlertState())
|
|
||||||
|
|
||||||
if evalContext.Rule.ExecutionErrorState == m.ExecutionErrorKeepState {
|
|
||||||
return evalContext.PrevAlertState
|
|
||||||
} else {
|
|
||||||
return evalContext.Rule.ExecutionErrorState.ToAlertState()
|
|
||||||
}
|
|
||||||
} else if evalContext.Firing {
|
|
||||||
return m.AlertStateAlerting
|
|
||||||
} else if evalContext.NoDataFound {
|
|
||||||
handler.log.Info("Alert Rule returned no data",
|
|
||||||
"ruleId", evalContext.Rule.Id,
|
|
||||||
"name", evalContext.Rule.Name,
|
|
||||||
"changing state to", evalContext.Rule.NoDataState.ToAlertState())
|
|
||||||
|
|
||||||
if evalContext.Rule.NoDataState == m.NoDataKeepState {
|
|
||||||
return evalContext.PrevAlertState
|
|
||||||
} else {
|
|
||||||
return evalContext.Rule.NoDataState.ToAlertState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.AlertStateOK
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
||||||
executionError := ""
|
executionError := ""
|
||||||
annotationData := simplejson.New()
|
annotationData := simplejson.New()
|
||||||
|
|
||||||
evalContext.Rule.State = handler.GetStateFromEvaluation(evalContext)
|
|
||||||
|
|
||||||
if evalContext.Error != nil {
|
if evalContext.Error != nil {
|
||||||
executionError = evalContext.Error.Error()
|
executionError = evalContext.Error.Error()
|
||||||
annotationData.Set("errorMessage", executionError)
|
annotationData.Set("errorMessage", executionError)
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
package alerting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAlertingResultHandler(t *testing.T) {
|
|
||||||
Convey("Result handler", t, func() {
|
|
||||||
ctx := NewEvalContext(context.TODO(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}})
|
|
||||||
dummieError := fmt.Errorf("dummie")
|
|
||||||
handler := NewResultHandler()
|
|
||||||
|
|
||||||
Convey("Should update alert state", func() {
|
|
||||||
|
|
||||||
Convey("ok -> alerting", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Firing = true
|
|
||||||
|
|
||||||
So(handler.GetStateFromEvaluation(ctx), ShouldEqual, models.AlertStateAlerting)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("ok -> error(alerting)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Error = dummieError
|
|
||||||
ctx.Rule.ExecutionErrorState = models.ExecutionErrorSetAlerting
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("ok -> error(keep_last)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Error = dummieError
|
|
||||||
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("pending -> error(keep_last)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStatePending
|
|
||||||
ctx.Error = dummieError
|
|
||||||
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("ok -> no_data(alerting)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Rule.NoDataState = models.NoDataSetAlerting
|
|
||||||
ctx.NoDataFound = true
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeTrue)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("ok -> no_data(keep_last)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
|
||||||
ctx.Rule.NoDataState = models.NoDataKeepState
|
|
||||||
ctx.NoDataFound = true
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
|
|
||||||
Convey("pending -> no_data(keep_last)", func() {
|
|
||||||
ctx.PrevAlertState = models.AlertStatePending
|
|
||||||
ctx.Rule.NoDataState = models.NoDataKeepState
|
|
||||||
ctx.NoDataFound = true
|
|
||||||
|
|
||||||
ctx.Rule.State = handler.GetStateFromEvaluation(ctx)
|
|
||||||
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending)
|
|
||||||
So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user