grafana/pkg/services/alerting/conditions/evaluator.go
Arve Knudsen 7897c6b7d5
Chore: Fix staticcheck issues (#28854)
* Chore: Fix issues reported by staticcheck

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Undo changes

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-05 11:57:20 +01:00

158 lines
3.8 KiB
Go

package conditions
import (
"encoding/json"
"fmt"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/alerting"
)
var (
defaultTypes = []string{"gt", "lt"}
rangedTypes = []string{"within_range", "outside_range"}
)
// AlertEvaluator evaluates the reduced value of a timeseries.
// Returning true if a timeseries is violating the condition
// ex: ThresholdEvaluator, NoValueEvaluator, RangeEvaluator
type AlertEvaluator interface {
Eval(reducedValue null.Float) bool
}
type noValueEvaluator struct{}
func (e *noValueEvaluator) Eval(reducedValue null.Float) bool {
return !reducedValue.Valid
}
type thresholdEvaluator struct {
Type string
Threshold float64
}
func newThresholdEvaluator(typ string, model *simplejson.Json) (*thresholdEvaluator, error) {
params := model.Get("params").MustArray()
if len(params) == 0 || params[0] == nil {
return nil, fmt.Errorf("evaluator '%v' is missing the threshold parameter", HumanThresholdType(typ))
}
firstParam, ok := params[0].(json.Number)
if !ok {
return nil, fmt.Errorf("evaluator has invalid parameter")
}
defaultEval := &thresholdEvaluator{Type: typ}
defaultEval.Threshold, _ = firstParam.Float64()
return defaultEval, nil
}
func (e *thresholdEvaluator) Eval(reducedValue null.Float) bool {
if !reducedValue.Valid {
return false
}
switch e.Type {
case "gt":
return reducedValue.Float64 > e.Threshold
case "lt":
return reducedValue.Float64 < e.Threshold
}
return false
}
type rangedEvaluator struct {
Type string
Lower float64
Upper float64
}
func newRangedEvaluator(typ string, model *simplejson.Json) (*rangedEvaluator, error) {
params := model.Get("params").MustArray()
if len(params) == 0 {
return nil, alerting.ValidationError{Reason: "Evaluator missing threshold parameter"}
}
firstParam, ok := params[0].(json.Number)
if !ok {
return nil, alerting.ValidationError{Reason: "Evaluator has invalid parameter"}
}
secondParam, ok := params[1].(json.Number)
if !ok {
return nil, alerting.ValidationError{Reason: "Evaluator has invalid second parameter"}
}
rangedEval := &rangedEvaluator{Type: typ}
rangedEval.Lower, _ = firstParam.Float64()
rangedEval.Upper, _ = secondParam.Float64()
return rangedEval, nil
}
func (e *rangedEvaluator) Eval(reducedValue null.Float) bool {
if !reducedValue.Valid {
return false
}
floatValue := reducedValue.Float64
switch e.Type {
case "within_range":
return (e.Lower < floatValue && e.Upper > floatValue) || (e.Upper < floatValue && e.Lower > floatValue)
case "outside_range":
return (e.Upper < floatValue && e.Lower < floatValue) || (e.Upper > floatValue && e.Lower > floatValue)
}
return false
}
// NewAlertEvaluator is a factory function for returning
// an `AlertEvaluator` depending on the json model.
func NewAlertEvaluator(model *simplejson.Json) (AlertEvaluator, error) {
typ := model.Get("type").MustString()
if typ == "" {
return nil, fmt.Errorf("evaluator missing type property")
}
if inSlice(typ, defaultTypes) {
return newThresholdEvaluator(typ, model)
}
if inSlice(typ, rangedTypes) {
return newRangedEvaluator(typ, model)
}
if typ == "no_value" {
return &noValueEvaluator{}, nil
}
return nil, fmt.Errorf("evaluator invalid evaluator type: %s", typ)
}
func inSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// HumanThresholdType converts a threshold "type" string to a string that matches the UI
// so errors are less confusing.
func HumanThresholdType(typ string) string {
switch typ {
case "gt":
return "IS ABOVE"
case "lt":
return "IS BELOW"
case "within_range":
return "IS WITHIN RANGE"
case "outside_range":
return "IS OUTSIDE RANGE"
}
return ""
}