1
0
Fork 0
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:
Kumar Mallikarjuna 2021-09-23 03:10:45 +05:30 committed by GitHub
parent e1daf2085d
commit 0616429267
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 203 additions and 2 deletions

View file

@ -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) {

View file

@ -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))
}

View file

@ -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)://")