mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +00:00
427 lines
8.9 KiB
Go
427 lines
8.9 KiB
Go
|
package jmespath
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"reflect"
|
||
|
"time"
|
||
|
|
||
|
"gopkg.in/inf.v0"
|
||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||
|
)
|
||
|
|
||
|
type Operand interface {
|
||
|
Add(interface{}) (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 ParseArithemticOperands(arguments []interface{}, operator string) (Operand, Operand, error) {
|
||
|
op := [2]Operand{nil, nil}
|
||
|
t := [2]int{0, 0}
|
||
|
|
||
|
for i := 0; i < 2; i++ {
|
||
|
tmp, err := validateArg(divide, arguments, i, reflect.Float64)
|
||
|
if err == nil {
|
||
|
var sc Scalar
|
||
|
sc.float64 = tmp.Float()
|
||
|
op[i] = sc
|
||
|
}
|
||
|
|
||
|
tmp, err = validateArg(divide, arguments, i, reflect.String)
|
||
|
if err == nil {
|
||
|
var q Quantity
|
||
|
q.Quantity, err = resource.ParseQuantity(tmp.String())
|
||
|
if err == nil {
|
||
|
op[i] = q
|
||
|
t[i] = 1
|
||
|
} else {
|
||
|
var d Duration
|
||
|
d.Duration, err = time.ParseDuration(tmp.String())
|
||
|
if err == nil {
|
||
|
op[i] = d
|
||
|
t[i] = 2
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if op[0] == nil || op[1] == nil || t[0]|t[1] == 3 {
|
||
|
return nil, nil, fmt.Errorf(genericError, operator, "invalid operands")
|
||
|
}
|
||
|
|
||
|
return op[0], op[1], nil
|
||
|
}
|
||
|
|
||
|
func (op1 Quantity) Add(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Quantity:
|
||
|
op1.Quantity.Add(v.Quantity)
|
||
|
return op1.String(), nil
|
||
|
case Scalar:
|
||
|
q, err := resource.ParseQuantity(fmt.Sprintf("%v", v.float64))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
op1.Quantity.Add(q)
|
||
|
return op1.String(), nil
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Duration) Add(op2 interface{}) (interface{}, error) {
|
||
|
var sum int64
|
||
|
|
||
|
switch v := op2.(type) {
|
||
|
case Duration:
|
||
|
sum = op1.Nanoseconds() + v.Nanoseconds()
|
||
|
case Scalar:
|
||
|
// Converting the duration to nanoseconds for more precision
|
||
|
sum = op1.Nanoseconds() + int64(v.float64*math.Pow10(9))
|
||
|
}
|
||
|
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%vns", sum))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
func (op1 Scalar) Add(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Scalar:
|
||
|
return op1.float64 + v.float64, nil
|
||
|
case Quantity:
|
||
|
return v.Add(op1)
|
||
|
case Duration:
|
||
|
return v.Add(op1)
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Quantity) Subtract(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Quantity:
|
||
|
op1.Quantity.Sub(v.Quantity)
|
||
|
return op1.String(), nil
|
||
|
case Scalar:
|
||
|
q, err := resource.ParseQuantity(fmt.Sprintf("%v", v.float64))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
op1.Quantity.Sub(q)
|
||
|
return op1.String(), nil
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Duration) Subtract(op2 interface{}) (interface{}, error) {
|
||
|
var diff int64
|
||
|
|
||
|
switch v := op2.(type) {
|
||
|
case Duration:
|
||
|
diff = op1.Nanoseconds() - v.Nanoseconds()
|
||
|
case Scalar:
|
||
|
// Converting the duration to nanoseconds for more precision
|
||
|
diff = op1.Nanoseconds() - int64(v.float64*math.Pow10(9))
|
||
|
}
|
||
|
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%vns", diff))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
func (op1 Scalar) Subtract(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Scalar:
|
||
|
return op1.float64 - v.float64, nil
|
||
|
case Quantity:
|
||
|
v.Neg()
|
||
|
return v.Add(op1)
|
||
|
case Duration:
|
||
|
// Converting the duration to nanoseconds for more precision
|
||
|
diff := int64(op1.float64*math.Pow10(9)) - v.Nanoseconds()
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%vns", diff))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Quantity) Multiply(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Quantity:
|
||
|
var prod inf.Dec
|
||
|
prod.Mul(op1.Quantity.AsDec(), v.Quantity.AsDec())
|
||
|
return resource.NewDecimalQuantity(prod, v.Quantity.Format).String(), nil
|
||
|
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
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Duration) Multiply(op2 interface{}) (interface{}, error) {
|
||
|
var prod float64
|
||
|
|
||
|
switch v := op2.(type) {
|
||
|
case Duration:
|
||
|
prod = op1.Seconds() * v.Seconds()
|
||
|
case Scalar:
|
||
|
prod = op1.Seconds() * v.float64
|
||
|
}
|
||
|
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%.9fs", prod))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Quantity) Divide(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Quantity:
|
||
|
var quo inf.Dec
|
||
|
scale := inf.Scale(math.Max(float64(op1.AsDec().Scale()), float64(v.AsDec().Scale())))
|
||
|
quo.QuoRound(op1.AsDec(), v.AsDec(), scale, inf.RoundDown)
|
||
|
return resource.NewDecimalQuantity(quo, v.Quantity.Format).String(), nil
|
||
|
case Scalar:
|
||
|
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
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Duration) Divide(op2 interface{}) (interface{}, error) {
|
||
|
var quo float64
|
||
|
|
||
|
switch v := op2.(type) {
|
||
|
case Duration:
|
||
|
if v.Seconds() == 0 {
|
||
|
return nil, fmt.Errorf(undefinedQuoError, divide)
|
||
|
}
|
||
|
|
||
|
quo = op1.Seconds() / v.Seconds()
|
||
|
case Scalar:
|
||
|
if v.float64 == 0 {
|
||
|
return nil, fmt.Errorf(undefinedQuoError, divide)
|
||
|
}
|
||
|
|
||
|
quo = op1.Seconds() / v.float64
|
||
|
}
|
||
|
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%.9fs", quo))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
func (op1 Scalar) Divide(op2 interface{}) (interface{}, error) {
|
||
|
switch v := op2.(type) {
|
||
|
case Scalar:
|
||
|
if v.float64 == 0 {
|
||
|
return nil, fmt.Errorf(zeroDivisionError, divide)
|
||
|
}
|
||
|
|
||
|
return op1.float64 / v.float64, nil
|
||
|
case Quantity:
|
||
|
q, err := resource.ParseQuantity(fmt.Sprintf("%v", op1.float64))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var quo inf.Dec
|
||
|
scale := inf.Scale(math.Max(float64(q.AsDec().Scale()), float64(v.AsDec().Scale())))
|
||
|
quo.QuoRound(q.AsDec(), v.AsDec(), scale, inf.RoundDown)
|
||
|
|
||
|
return resource.NewDecimalQuantity(quo, v.Format).String(), nil
|
||
|
case Duration:
|
||
|
var quo float64
|
||
|
if op1.float64 == 0 {
|
||
|
return nil, fmt.Errorf(undefinedQuoError, divide)
|
||
|
}
|
||
|
|
||
|
quo = op1.float64 / v.Seconds()
|
||
|
|
||
|
res, err := time.ParseDuration(fmt.Sprintf("%.9fs", quo))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return res.String(), nil
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func (op1 Quantity) Modulo(op2 interface{}) (interface{}, error) {
|
||
|
quo, err := op1.Divide(op2)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
x, err := resource.ParseQuantity(quo.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
mul, err := op2.(Operand).Multiply(Quantity{x})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
y, err := resource.ParseQuantity(mul.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return op1.Subtract(Quantity{y})
|
||
|
}
|
||
|
|
||
|
func (op1 Duration) Modulo(op2 interface{}) (interface{}, error) {
|
||
|
quo, err := op1.Divide(op2)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
x, err := time.ParseDuration(quo.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
x = x.Truncate(time.Second)
|
||
|
|
||
|
mul, err := op2.(Operand).Multiply(Duration{x})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
y, err := time.ParseDuration(mul.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return op1.Subtract(Duration{y})
|
||
|
}
|
||
|
|
||
|
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, fmt.Errorf(nonIntModuloError, modulo)
|
||
|
}
|
||
|
|
||
|
if v.float64 != float64(val2) {
|
||
|
return nil, fmt.Errorf(nonIntModuloError, modulo)
|
||
|
}
|
||
|
|
||
|
if val2 == 0 {
|
||
|
return nil, fmt.Errorf(zeroDivisionError, modulo)
|
||
|
}
|
||
|
|
||
|
return float64(val1 % val2), nil
|
||
|
case Quantity:
|
||
|
quo, err := op1.Divide(op2)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
x, err := resource.ParseQuantity(quo.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
mul, err := op2.(Operand).Multiply(Quantity{x})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
y, err := resource.ParseQuantity(mul.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return op1.Subtract(Quantity{y})
|
||
|
case Duration:
|
||
|
quo, err := op1.Divide(op2)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
x, err := time.ParseDuration(quo.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
x = x.Truncate(time.Second)
|
||
|
|
||
|
mul, err := op2.(Operand).Multiply(Duration{x})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
y, err := time.ParseDuration(mul.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return op1.Subtract(Duration{y})
|
||
|
}
|
||
|
|
||
|
return nil, nil
|
||
|
}
|