Alerting: Improve validation of query and expressions on rule submit (#53258)

* Improve error messages of server-side expression 
* move validation of alert queries and a condition to eval package
This commit is contained in:
Yuriy Tseretyan
2022-09-21 15:14:11 -04:00
committed by GitHub
parent 879241a48f
commit 2d38664fe6
19 changed files with 311 additions and 146 deletions
+20 -19
View File
@@ -2,6 +2,7 @@ package expr
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -43,16 +44,16 @@ func NewMathCommand(refID, expr string) (*MathCommand, error) {
func UnmarshalMathCommand(rn *rawNode) (*MathCommand, error) {
rawExpr, ok := rn.Query["expression"]
if !ok {
return nil, fmt.Errorf("math command for refId %v is missing an expression", rn.RefID)
return nil, errors.New("command is missing an expression")
}
exprString, ok := rawExpr.(string)
if !ok {
return nil, fmt.Errorf("expected math command for refId %v expression to be a string, got %T", rn.RefID, rawExpr)
return nil, fmt.Errorf("math expression is expected to be a string, got %T", rawExpr)
}
gm, err := NewMathCommand(rn.RefID, exprString)
if err != nil {
return nil, fmt.Errorf("invalid math command type in '%v': %v", rn.RefID, err)
return nil, fmt.Errorf("invalid math command type: %w", err)
}
return gm, nil
}
@@ -96,21 +97,21 @@ func NewReduceCommand(refID, reducer, varToReduce string, mapper mathexp.ReduceM
func UnmarshalReduceCommand(rn *rawNode) (*ReduceCommand, error) {
rawVar, ok := rn.Query["expression"]
if !ok {
return nil, fmt.Errorf("no variable specified to reduce for refId %v", rn.RefID)
return nil, errors.New("no expression ID is specified to reduce. Must be a reference to an existing query or expression")
}
varToReduce, ok := rawVar.(string)
if !ok {
return nil, fmt.Errorf("expected reduce variable to be a string, got %T for refId %v", rawVar, rn.RefID)
return nil, fmt.Errorf("expression ID is expected to be a string, got %T", rawVar)
}
varToReduce = strings.TrimPrefix(varToReduce, "$")
rawReducer, ok := rn.Query["reducer"]
if !ok {
return nil, fmt.Errorf("no reducer specified for refId %v", rn.RefID)
return nil, errors.New("no reducer specified")
}
redFunc, ok := rawReducer.(string)
if !ok {
return nil, fmt.Errorf("expected reducer to be a string, got %T for refId %v", rawReducer, rn.RefID)
return nil, fmt.Errorf("expected reducer to be a string, got %T", rawReducer)
}
var mapper mathexp.ReduceMapper = nil
@@ -126,20 +127,20 @@ func UnmarshalReduceCommand(rn *rawNode) (*ReduceCommand, error) {
case "replaceNN":
valueStr, ok := s["replaceWithValue"]
if !ok {
return nil, fmt.Errorf("expected settings.replaceWithValue to be specified when mode is 'replaceNN' for refId %v", rn.RefID)
return nil, errors.New("setting replaceWithValue must be specified when mode is 'replaceNN'")
}
switch value := valueStr.(type) {
case float64:
mapper = mathexp.ReplaceNonNumberWithValue{Value: value}
default:
return nil, fmt.Errorf("expected settings.replaceWithValue to be a number, got %T for refId %v", value, rn.RefID)
return nil, fmt.Errorf("setting replaceWithValue must be a number, got %T", value)
}
default:
return nil, fmt.Errorf("reducer mode %s is not supported for refId %v. Supported only: [dropNN,replaceNN]", mode, rn.RefID)
return nil, fmt.Errorf("reducer mode '%s' is not supported. Supported only: [dropNN,replaceNN]", mode)
}
}
default:
return nil, fmt.Errorf("expected settings to be an object, got %T for refId %v", s, rn.RefID)
return nil, fmt.Errorf("field settings must be an object, got %T for refId %v", s, rn.RefID)
}
}
return NewReduceCommand(rn.RefID, redFunc, varToReduce, mapper)
@@ -211,40 +212,40 @@ func NewResampleCommand(refID, rawWindow, varToResample string, downsampler stri
func UnmarshalResampleCommand(rn *rawNode) (*ResampleCommand, error) {
rawVar, ok := rn.Query["expression"]
if !ok {
return nil, fmt.Errorf("no variable to resample for refId %v", rn.RefID)
return nil, errors.New("no expression ID to resample. must be a reference to an existing query or expression")
}
varToReduce, ok := rawVar.(string)
if !ok {
return nil, fmt.Errorf("expected resample input variable to be type string, but got type %T for refId %v", rawVar, rn.RefID)
return nil, fmt.Errorf("expected resample input variable to be type string, but got type %T", rawVar)
}
varToReduce = strings.TrimPrefix(varToReduce, "$")
varToResample := varToReduce
rawWindow, ok := rn.Query["window"]
if !ok {
return nil, fmt.Errorf("no time duration specified for the window in resample command for refId %v", rn.RefID)
return nil, errors.New("no time duration specified for the window in resample command")
}
window, ok := rawWindow.(string)
if !ok {
return nil, fmt.Errorf("expected resample window to be a string, got %T for refId %v", rawWindow, rn.RefID)
return nil, fmt.Errorf("resample window is expected to be a string, got %T", rawWindow)
}
rawDownsampler, ok := rn.Query["downsampler"]
if !ok {
return nil, fmt.Errorf("no downsampler function specified in resample command for refId %v", rn.RefID)
return nil, errors.New("no downsampler function specified in resample command")
}
downsampler, ok := rawDownsampler.(string)
if !ok {
return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T for refId %v", downsampler, rn.RefID)
return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T", downsampler)
}
rawUpsampler, ok := rn.Query["upsampler"]
if !ok {
return nil, fmt.Errorf("no downsampler specified in resample command for refId %v", rn.RefID)
return nil, errors.New("no upsampler specified in resample command")
}
upsampler, ok := rawUpsampler.(string)
if !ok {
return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T for refId %v", upsampler, rn.RefID)
return nil, fmt.Errorf("expected resample downsampler to be a string, got type %T", upsampler)
}
return NewResampleCommand(rn.RefID, window, varToResample, downsampler, upsampler, rn.TimeRange)