mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 12:44:10 -06:00
7897c6b7d5
* 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>
158 lines
3.8 KiB
Go
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 ""
|
|
}
|