mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
998a14c660
* adding roundoff Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * removing unnecessary Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * adding test Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * adding edge case Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * fixing error Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * updating function call Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * updating function jpRound Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * error handling negative Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * fix Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> * fix linter Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * parsing Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * cleanup Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Rexbeast2 <ssukhveer514@gmail.com> Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
298 lines
7.8 KiB
Go
298 lines
7.8 KiB
Go
package jmespath
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"time"
|
|
|
|
"gopkg.in/inf.v0"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
)
|
|
|
|
type operand interface {
|
|
Add(interface{}, string) (interface{}, error)
|
|
Subtract(interface{}) (interface{}, error)
|
|
Multiply(interface{}) (interface{}, error)
|
|
Divide(interface{}) (interface{}, error)
|
|
Modulo(interface{}) (interface{}, error)
|
|
}
|
|
|
|
type quantity struct {
|
|
resource.Quantity
|
|
}
|
|
|
|
type duration struct {
|
|
time.Duration
|
|
}
|
|
|
|
type scalar struct {
|
|
float64
|
|
}
|
|
|
|
func parseArithemticOperand(arguments []interface{}, index int, operator string) (operand, error) {
|
|
if tmp, err := validateArg(operator, arguments, index, reflect.Float64); err == nil {
|
|
return scalar{float64: tmp.Float()}, nil
|
|
} else if tmp, err = validateArg(operator, arguments, index, reflect.String); err == nil {
|
|
if q, err := resource.ParseQuantity(tmp.String()); err == nil {
|
|
return quantity{Quantity: q}, nil
|
|
} else if d, err := time.ParseDuration(tmp.String()); err == nil {
|
|
return duration{Duration: d}, nil
|
|
}
|
|
}
|
|
return nil, formatError(genericError, operator, "invalid operand")
|
|
}
|
|
|
|
func parseArithemticOperands(arguments []interface{}, operator string) (operand, operand, error) {
|
|
left, err := parseArithemticOperand(arguments, 0, operator)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
right, err := parseArithemticOperand(arguments, 1, operator)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return left, right, nil
|
|
}
|
|
|
|
// Quantity +|- Quantity -> Quantity
|
|
// Quantity +|- Duration|Scalar -> error
|
|
// Duration +|- Duration -> Duration
|
|
// Duration +|- Quantity|Scalar -> error
|
|
// Scalar +|- Scalar -> Scalar
|
|
// Scalar +|- Quantity|Duration -> error
|
|
|
|
func (op1 quantity) Add(op2 interface{}, operator string) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case quantity:
|
|
op1.Quantity.Add(v.Quantity)
|
|
return op1.String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, operator)
|
|
}
|
|
}
|
|
|
|
func (op1 duration) Add(op2 interface{}, operator string) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case duration:
|
|
return (op1.Duration + v.Duration).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, operator)
|
|
}
|
|
}
|
|
|
|
func (op1 scalar) Add(op2 interface{}, operator string) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
return op1.float64 + v.float64, nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, operator)
|
|
}
|
|
}
|
|
|
|
func (op1 quantity) Subtract(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case quantity:
|
|
op1.Quantity.Sub(v.Quantity)
|
|
return op1.String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, subtract)
|
|
}
|
|
}
|
|
|
|
func (op1 duration) Subtract(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case duration:
|
|
return (op1.Duration - v.Duration).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, subtract)
|
|
}
|
|
}
|
|
|
|
func (op1 scalar) Subtract(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
return op1.float64 - v.float64, nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, subtract)
|
|
}
|
|
}
|
|
|
|
// Quantity * Quantity|Duration -> error
|
|
// Quantity * Scalar -> Quantity
|
|
|
|
// Duration * Quantity|Duration -> error
|
|
// Duration * Scalar -> Duration
|
|
|
|
// Scalar * Scalar -> Scalar
|
|
// Scalar * Quantity -> Quantity
|
|
// Scalar * Duration -> Duration
|
|
|
|
func (op1 quantity) Multiply(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
q, err := resource.ParseQuantity(fmt.Sprintf("%v", v.float64))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var prod inf.Dec
|
|
prod.Mul(op1.Quantity.AsDec(), q.AsDec())
|
|
return resource.NewDecimalQuantity(prod, op1.Quantity.Format).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, multiply)
|
|
}
|
|
}
|
|
|
|
func (op1 duration) Multiply(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
seconds := op1.Seconds() * v.float64
|
|
return time.Duration(seconds * float64(time.Second)).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, multiply)
|
|
}
|
|
}
|
|
|
|
func (op1 scalar) Multiply(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
return op1.float64 * v.float64, nil
|
|
case quantity:
|
|
return v.Multiply(op1)
|
|
case duration:
|
|
return v.Multiply(op1)
|
|
default:
|
|
return nil, formatError(typeMismatchError, multiply)
|
|
}
|
|
}
|
|
|
|
// Quantity / Duration -> error
|
|
// Quantity / Quantity -> Scalar
|
|
// Quantity / Scalar -> Quantity
|
|
|
|
// Duration / Quantity -> error
|
|
// Duration / Duration -> Scalar
|
|
// Duration / Scalar -> Duration
|
|
|
|
// Scalar / Scalar -> Scalar
|
|
// Scalar / Quantity -> error
|
|
// Scalar / Duration -> error
|
|
|
|
func (op1 quantity) Divide(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case quantity:
|
|
divisor := v.AsApproximateFloat64()
|
|
if divisor == 0 {
|
|
return nil, formatError(zeroDivisionError, divide)
|
|
}
|
|
dividend := op1.AsApproximateFloat64()
|
|
return dividend / divisor, nil
|
|
case scalar:
|
|
if v.float64 == 0 {
|
|
return nil, formatError(zeroDivisionError, divide)
|
|
}
|
|
q, err := resource.ParseQuantity(fmt.Sprintf("%v", v.float64))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var quo inf.Dec
|
|
scale := inf.Scale(math.Max(float64(op1.AsDec().Scale()), float64(q.AsDec().Scale())))
|
|
quo.QuoRound(op1.AsDec(), q.AsDec(), scale, inf.RoundDown)
|
|
return resource.NewDecimalQuantity(quo, op1.Quantity.Format).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, divide)
|
|
}
|
|
}
|
|
|
|
func (op1 duration) Divide(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case duration:
|
|
if v.Seconds() == 0 {
|
|
return nil, formatError(zeroDivisionError, divide)
|
|
}
|
|
return op1.Seconds() / v.Seconds(), nil
|
|
case scalar:
|
|
if v.float64 == 0 {
|
|
return nil, formatError(zeroDivisionError, divide)
|
|
}
|
|
seconds := op1.Seconds() / v.float64
|
|
return time.Duration(seconds * float64(time.Second)).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, divide)
|
|
}
|
|
}
|
|
|
|
func (op1 scalar) Divide(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
if v.float64 == 0 {
|
|
return nil, formatError(zeroDivisionError, divide)
|
|
}
|
|
return op1.float64 / v.float64, nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, divide)
|
|
}
|
|
}
|
|
|
|
// Quantity % Duration|Scalar -> error
|
|
// Quantity % Quantity -> Quantity
|
|
|
|
// Duration % Quantity|Scalar -> error
|
|
// Duration % Duration -> Duration
|
|
|
|
// Scalar % Quantity|Duration -> error
|
|
// Scalar % Scalar -> Scalar
|
|
|
|
func (op1 quantity) Modulo(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case quantity:
|
|
f1 := op1.ToDec().AsApproximateFloat64()
|
|
f2 := v.ToDec().AsApproximateFloat64()
|
|
i1 := int64(f1)
|
|
i2 := int64(f2)
|
|
if f1 != float64(i1) {
|
|
return nil, formatError(nonIntModuloError, modulo)
|
|
}
|
|
if f2 != float64(i2) {
|
|
return nil, formatError(nonIntModuloError, modulo)
|
|
}
|
|
if i2 == 0 {
|
|
return nil, formatError(zeroDivisionError, modulo)
|
|
}
|
|
return resource.NewQuantity(i1%i2, op1.Quantity.Format).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, modulo)
|
|
}
|
|
}
|
|
|
|
func (op1 duration) Modulo(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case duration:
|
|
if v.Duration == 0 {
|
|
return nil, formatError(zeroDivisionError, modulo)
|
|
}
|
|
return (op1.Duration % v.Duration).String(), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, modulo)
|
|
}
|
|
}
|
|
|
|
func (op1 scalar) Modulo(op2 interface{}) (interface{}, error) {
|
|
switch v := op2.(type) {
|
|
case scalar:
|
|
val1 := int64(op1.float64)
|
|
val2 := int64(v.float64)
|
|
if op1.float64 != float64(val1) {
|
|
return nil, formatError(nonIntModuloError, modulo)
|
|
}
|
|
if v.float64 != float64(val2) {
|
|
return nil, formatError(nonIntModuloError, modulo)
|
|
}
|
|
if val2 == 0 {
|
|
return nil, formatError(zeroDivisionError, modulo)
|
|
}
|
|
return float64(val1 % val2), nil
|
|
default:
|
|
return nil, formatError(typeMismatchError, modulo)
|
|
}
|
|
}
|