2021-04-23 14:32:25 -05:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
|
|
|
ngModels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
|
|
)
|
|
|
|
|
|
|
|
type State struct {
|
|
|
|
AlertRuleUID string
|
|
|
|
OrgID int64
|
|
|
|
CacheId string
|
|
|
|
State eval.State
|
|
|
|
Results []Evaluation
|
|
|
|
StartsAt time.Time
|
|
|
|
EndsAt time.Time
|
|
|
|
LastEvaluationTime time.Time
|
|
|
|
EvaluationDuration time.Duration
|
2021-05-19 15:15:09 -05:00
|
|
|
LastSentAt time.Time
|
2021-04-23 14:32:25 -05:00
|
|
|
Annotations map[string]string
|
2021-05-19 15:15:09 -05:00
|
|
|
Labels data.Labels
|
2021-05-04 12:08:12 -05:00
|
|
|
Error error
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type Evaluation struct {
|
2021-05-18 08:12:39 -05:00
|
|
|
EvaluationTime time.Time
|
|
|
|
EvaluationState eval.State
|
|
|
|
EvaluationString string
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
|
|
|
|
2021-06-17 12:01:46 -05:00
|
|
|
func (a *State) resultNormal(result eval.Result) {
|
2021-05-26 13:37:42 -05:00
|
|
|
if a.State != eval.Normal {
|
|
|
|
a.EndsAt = result.EvaluatedAt
|
|
|
|
a.StartsAt = result.EvaluatedAt
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
2021-05-26 13:37:42 -05:00
|
|
|
a.Error = result.Error // should be nil since state is not error
|
|
|
|
a.State = eval.Normal
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
|
|
|
|
2021-06-17 12:01:46 -05:00
|
|
|
func (a *State) resultAlerting(alertRule *ngModels.AlertRule, result eval.Result) {
|
2021-04-23 14:32:25 -05:00
|
|
|
switch a.State {
|
|
|
|
case eval.Alerting:
|
2021-06-17 12:01:46 -05:00
|
|
|
a.setEndsAt(alertRule, result)
|
2021-04-23 14:32:25 -05:00
|
|
|
case eval.Pending:
|
|
|
|
if result.EvaluatedAt.Sub(a.StartsAt) > alertRule.For {
|
|
|
|
a.State = eval.Alerting
|
|
|
|
a.StartsAt = result.EvaluatedAt
|
2021-06-17 12:01:46 -05:00
|
|
|
a.setEndsAt(alertRule, result)
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
a.StartsAt = result.EvaluatedAt
|
2021-06-17 12:01:46 -05:00
|
|
|
a.setEndsAt(alertRule, result)
|
2021-04-23 14:32:25 -05:00
|
|
|
if !(alertRule.For > 0) {
|
2021-06-17 12:01:46 -05:00
|
|
|
// If For is 0, immediately set Alerting
|
2021-04-23 14:32:25 -05:00
|
|
|
a.State = eval.Alerting
|
|
|
|
} else {
|
2021-06-17 12:01:46 -05:00
|
|
|
a.State = eval.Pending
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 12:01:46 -05:00
|
|
|
func (a *State) resultError(alertRule *ngModels.AlertRule, result eval.Result) {
|
2021-05-04 12:08:12 -05:00
|
|
|
a.Error = result.Error
|
2021-04-23 14:32:25 -05:00
|
|
|
if a.StartsAt.IsZero() {
|
|
|
|
a.StartsAt = result.EvaluatedAt
|
|
|
|
}
|
2021-06-17 12:01:46 -05:00
|
|
|
a.setEndsAt(alertRule, result)
|
2021-04-23 14:32:25 -05:00
|
|
|
|
2021-06-17 12:01:46 -05:00
|
|
|
if alertRule.ExecErrState == ngModels.AlertingErrState {
|
2021-04-23 14:32:25 -05:00
|
|
|
a.State = eval.Alerting
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 12:01:46 -05:00
|
|
|
func (a *State) resultNoData(alertRule *ngModels.AlertRule, result eval.Result) {
|
2021-04-23 14:32:25 -05:00
|
|
|
if a.StartsAt.IsZero() {
|
|
|
|
a.StartsAt = result.EvaluatedAt
|
|
|
|
}
|
2021-06-17 12:01:46 -05:00
|
|
|
a.setEndsAt(alertRule, result)
|
2021-04-23 14:32:25 -05:00
|
|
|
|
|
|
|
switch alertRule.NoDataState {
|
|
|
|
case ngModels.Alerting:
|
|
|
|
a.State = eval.Alerting
|
|
|
|
case ngModels.NoData:
|
|
|
|
a.State = eval.NoData
|
|
|
|
case ngModels.OK:
|
|
|
|
a.State = eval.Normal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 15:15:09 -05:00
|
|
|
func (a *State) NeedsSending(resendDelay time.Duration) bool {
|
|
|
|
if a.State != eval.Alerting {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// if LastSentAt is before or equal to LastEvaluationTime + resendDelay, send again
|
|
|
|
return a.LastSentAt.Add(resendDelay).Before(a.LastEvaluationTime) ||
|
|
|
|
a.LastSentAt.Add(resendDelay).Equal(a.LastEvaluationTime)
|
|
|
|
}
|
|
|
|
|
2021-04-23 14:32:25 -05:00
|
|
|
func (a *State) Equals(b *State) bool {
|
|
|
|
return a.AlertRuleUID == b.AlertRuleUID &&
|
|
|
|
a.OrgID == b.OrgID &&
|
|
|
|
a.CacheId == b.CacheId &&
|
|
|
|
a.Labels.String() == b.Labels.String() &&
|
|
|
|
a.State.String() == b.State.String() &&
|
|
|
|
a.StartsAt == b.StartsAt &&
|
|
|
|
a.EndsAt == b.EndsAt &&
|
2021-04-30 13:23:12 -05:00
|
|
|
a.LastEvaluationTime == b.LastEvaluationTime &&
|
|
|
|
data.Labels(a.Annotations).String() == data.Labels(b.Annotations).String()
|
2021-04-23 14:32:25 -05:00
|
|
|
}
|
2021-05-18 12:56:14 -05:00
|
|
|
|
|
|
|
func (a *State) TrimResults(alertRule *ngModels.AlertRule) {
|
|
|
|
numBuckets := 2 * (int64(alertRule.For.Seconds()) / alertRule.IntervalSeconds)
|
|
|
|
if numBuckets == 0 {
|
|
|
|
numBuckets = 10 // keep at least 10 evaluations in the event For is set to 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(a.Results) < int(numBuckets) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newResults := make([]Evaluation, numBuckets)
|
|
|
|
copy(newResults, a.Results[len(a.Results)-int(numBuckets):])
|
|
|
|
a.Results = newResults
|
|
|
|
}
|
2021-06-17 12:01:46 -05:00
|
|
|
|
|
|
|
func (a *State) setEndsAt(alertRule *ngModels.AlertRule, result eval.Result) {
|
|
|
|
if int64(alertRule.For.Seconds()) > alertRule.IntervalSeconds {
|
|
|
|
// For is set and longer than IntervalSeconds
|
|
|
|
a.EndsAt = result.EvaluatedAt.Add(alertRule.For)
|
|
|
|
} else {
|
|
|
|
// For is not set or is less than or equal to IntervalSeconds
|
|
|
|
a.EndsAt = result.EvaluatedAt.Add(time.Duration(alertRule.IntervalSeconds*2) * time.Second)
|
|
|
|
}
|
|
|
|
}
|