mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
JMESPath: Arithmetic Operators (#2416)
Signed-off-by: Kumar Mallikarjuna <kumarmallikarjuna1@gmail.com>
This commit is contained in:
parent
e1daf2085d
commit
0616429267
3 changed files with 203 additions and 2 deletions
|
@ -38,11 +38,18 @@ var (
|
|||
regexReplaceAllLiteral = "regex_replace_all_literal"
|
||||
regexMatch = "regex_match"
|
||||
labelMatch = "label_match"
|
||||
add = "add"
|
||||
subtract = "subtract"
|
||||
multiply = "multiply"
|
||||
divide = "divide"
|
||||
modulo = "modulo"
|
||||
)
|
||||
|
||||
const errorPrefix = "JMESPath function '%s': "
|
||||
const invalidArgumentTypeError = errorPrefix + "%d argument is expected of %s type"
|
||||
const genericError = errorPrefix + "%s"
|
||||
const zeroDivisionError = errorPrefix + "Zero divisor passed"
|
||||
const nonIntModuloError = errorPrefix + "Non-integer argument(s) passed for modulo"
|
||||
|
||||
func getFunctions() []*gojmespath.FunctionEntry {
|
||||
return []*gojmespath.FunctionEntry{
|
||||
|
@ -146,6 +153,46 @@ func getFunctions() []*gojmespath.FunctionEntry {
|
|||
},
|
||||
Handler: jpLabelMatch,
|
||||
},
|
||||
{
|
||||
Name: add,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpNumber}},
|
||||
{Types: []JpType{JpNumber}},
|
||||
},
|
||||
Handler: jpAdd,
|
||||
},
|
||||
{
|
||||
Name: subtract,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpNumber}},
|
||||
{Types: []JpType{JpNumber}},
|
||||
},
|
||||
Handler: jpSubtract,
|
||||
},
|
||||
{
|
||||
Name: multiply,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpNumber}},
|
||||
{Types: []JpType{JpNumber}},
|
||||
},
|
||||
Handler: jpMultiply,
|
||||
},
|
||||
{
|
||||
Name: divide,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpNumber}},
|
||||
{Types: []JpType{JpNumber}},
|
||||
},
|
||||
Handler: jpDivide,
|
||||
},
|
||||
{
|
||||
Name: modulo,
|
||||
Arguments: []ArgSpec{
|
||||
{Types: []JpType{JpNumber}},
|
||||
{Types: []JpType{JpNumber}},
|
||||
},
|
||||
Handler: jpModulo,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -360,6 +407,100 @@ func jpLabelMatch(arguments []interface{}) (interface{}, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func jpAdd(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
op1, err := validateArg(divide, arguments, 0, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op2, err := validateArg(divide, arguments, 1, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return op1.Float() + op2.Float(), nil
|
||||
}
|
||||
|
||||
func jpSubtract(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
op1, err := validateArg(divide, arguments, 0, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op2, err := validateArg(divide, arguments, 1, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return op1.Float() - op2.Float(), nil
|
||||
}
|
||||
|
||||
func jpMultiply(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
op1, err := validateArg(divide, arguments, 0, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op2, err := validateArg(divide, arguments, 1, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return op1.Float() * op2.Float(), nil
|
||||
}
|
||||
|
||||
func jpDivide(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
op1, err := validateArg(divide, arguments, 0, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op2, err := validateArg(divide, arguments, 1, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if op2.Float() == 0 {
|
||||
return nil, fmt.Errorf(zeroDivisionError, divide)
|
||||
}
|
||||
|
||||
return op1.Float() / op2.Float(), nil
|
||||
}
|
||||
|
||||
func jpModulo(arguments []interface{}) (interface{}, error) {
|
||||
var err error
|
||||
op1, err := validateArg(divide, arguments, 0, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op2, err := validateArg(divide, arguments, 1, reflect.Float64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val1 := int64(op1.Float())
|
||||
val2 := int64(op2.Float())
|
||||
|
||||
if op1.Float() != float64(val1) {
|
||||
return nil, fmt.Errorf(nonIntModuloError, modulo)
|
||||
}
|
||||
|
||||
if op2.Float() != float64(val2) {
|
||||
return nil, fmt.Errorf(nonIntModuloError, modulo)
|
||||
}
|
||||
|
||||
if val2 == 0 {
|
||||
return nil, fmt.Errorf(zeroDivisionError, modulo)
|
||||
}
|
||||
|
||||
return val1 % val2, nil
|
||||
}
|
||||
|
||||
// InterfaceToString casts an interface to a string type
|
||||
func ifaceToString(iface interface{}) (string, error) {
|
||||
switch iface.(type) {
|
||||
|
|
|
@ -301,3 +301,63 @@ func Test_labelMatch(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Add(t *testing.T) {
|
||||
jp, err := New("add(`12`, `13`)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
equal, ok := result.(float64)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, 25.0)
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Subtract(t *testing.T) {
|
||||
jp, err := New("subtract(`12`, `7`)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
equal, ok := result.(float64)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, 5.0)
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Multiply(t *testing.T) {
|
||||
jp, err := New("multiply(`3`, `2.5`)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
equal, ok := result.(float64)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, 7.5)
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Divide(t *testing.T) {
|
||||
jp, err := New("divide(`12`, `1.5`)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
equal, ok := result.(float64)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, 8.0)
|
||||
}
|
||||
|
||||
func TestJMESPathFunctions_Modulo(t *testing.T) {
|
||||
jp, err := New("modulo(`12`, `7`)")
|
||||
assert.NilError(t, err)
|
||||
|
||||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
equal, ok := result.(int64)
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, equal, int64(5))
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
// RegexVariables represents regex for '{{}}'
|
||||
var RegexVariables = regexp.MustCompile(`\{\{[^{}]*\}\}`)
|
||||
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}} and {{@}}
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*[request\.|serviceAccountName|serviceAccountNamespace|@][^{}]*\}\}`)
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}} and {{divide(<num>,<num>))}} (#2409)
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*[request\.|serviceAccountName|serviceAccountNamespace|@|divide][^{}]*\}\}`)
|
||||
|
||||
// IsHttpRegex represents regex for starts with http:// or https://
|
||||
var IsHttpRegex = regexp.MustCompile("^(http|https)://")
|
||||
|
|
Loading…
Add table
Reference in a new issue