mirror of
https://github.com/kyverno/kyverno.git
synced 2025-01-20 18:52:16 +00:00
2a375fa1b5
* remove contains function Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com> * added test for contains issue case Signed-off-by: Maxim Goncharenko <goncharenko.maxim@apriorit.com>
388 lines
9 KiB
Go
388 lines
9 KiB
Go
package jmespath
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
gojmespath "github.com/jmespath/go-jmespath"
|
|
)
|
|
|
|
var (
|
|
JpObject = gojmespath.JpObject
|
|
JpString = gojmespath.JpString
|
|
JpNumber = gojmespath.JpNumber
|
|
JpArray = gojmespath.JpArray
|
|
JpArrayString = gojmespath.JpArrayString
|
|
)
|
|
|
|
type (
|
|
JpType = gojmespath.JpType
|
|
ArgSpec = gojmespath.ArgSpec
|
|
)
|
|
|
|
// function names
|
|
var (
|
|
compare = "compare"
|
|
equalFold = "equal_fold"
|
|
replace = "replace"
|
|
replaceAll = "replace_all"
|
|
toUpper = "to_upper"
|
|
toLower = "to_lower"
|
|
trim = "trim"
|
|
split = "split"
|
|
regexReplaceAll = "regex_replace_all"
|
|
regexReplaceAllLiteral = "regex_replace_all_literal"
|
|
regexMatch = "regex_match"
|
|
labelMatch = "label_match"
|
|
)
|
|
|
|
const errorPrefix = "JMESPath function '%s': "
|
|
const invalidArgumentTypeError = errorPrefix + "%d argument is expected of %s type"
|
|
const genericError = errorPrefix + "%s"
|
|
|
|
func getFunctions() []*gojmespath.FunctionEntry {
|
|
return []*gojmespath.FunctionEntry{
|
|
{
|
|
Name: compare,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfCompare,
|
|
},
|
|
{
|
|
Name: equalFold,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfEqualFold,
|
|
},
|
|
{
|
|
Name: replace,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpNumber}},
|
|
},
|
|
Handler: jpfReplace,
|
|
},
|
|
{
|
|
Name: replaceAll,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfReplaceAll,
|
|
},
|
|
{
|
|
Name: toUpper,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfToUpper,
|
|
},
|
|
{
|
|
Name: toLower,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfToLower,
|
|
},
|
|
{
|
|
Name: trim,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfTrim,
|
|
},
|
|
{
|
|
Name: split,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString}},
|
|
},
|
|
Handler: jpfSplit,
|
|
},
|
|
{
|
|
Name: regexReplaceAll,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString, JpNumber}},
|
|
{Types: []JpType{JpString, JpNumber}},
|
|
},
|
|
Handler: jpRegexReplaceAll,
|
|
},
|
|
{
|
|
Name: regexReplaceAllLiteral,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString, JpNumber}},
|
|
{Types: []JpType{JpString, JpNumber}},
|
|
},
|
|
Handler: jpRegexReplaceAllLiteral,
|
|
},
|
|
{
|
|
Name: regexMatch,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpString}},
|
|
{Types: []JpType{JpString, JpNumber}},
|
|
},
|
|
Handler: jpRegexMatch,
|
|
},
|
|
{
|
|
// Validates if label (param1) would match pod/host/etc labels (param2)
|
|
Name: labelMatch,
|
|
Arguments: []ArgSpec{
|
|
{Types: []JpType{JpObject}},
|
|
{Types: []JpType{JpObject}},
|
|
},
|
|
Handler: jpLabelMatch,
|
|
},
|
|
}
|
|
|
|
}
|
|
|
|
func jpfCompare(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
a, err := validateArg(compare, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err := validateArg(compare, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.Compare(a.String(), b.String()), nil
|
|
}
|
|
|
|
func jpfEqualFold(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
a, err := validateArg(equalFold, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err := validateArg(equalFold, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.EqualFold(a.String(), b.String()), nil
|
|
}
|
|
|
|
func jpfReplace(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(replace, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
old, err := validateArg(replace, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
new, err := validateArg(replace, arguments, 2, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
n, err := validateArg(replace, arguments, 3, reflect.Float64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.Replace(str.String(), old.String(), new.String(), int(n.Float())), nil
|
|
}
|
|
|
|
func jpfReplaceAll(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(replaceAll, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
old, err := validateArg(replaceAll, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
new, err := validateArg(replaceAll, arguments, 2, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.ReplaceAll(str.String(), old.String(), new.String()), nil
|
|
}
|
|
|
|
func jpfToUpper(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(toUpper, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.ToUpper(str.String()), nil
|
|
}
|
|
|
|
func jpfToLower(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(toLower, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.ToLower(str.String()), nil
|
|
}
|
|
|
|
func jpfTrim(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(trim, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cutset, err := validateArg(trim, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.Trim(str.String(), cutset.String()), nil
|
|
}
|
|
|
|
func jpfSplit(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
str, err := validateArg(split, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sep, err := validateArg(split, arguments, 1, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return strings.Split(str.String(), sep.String()), nil
|
|
}
|
|
|
|
func jpRegexReplaceAll(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexReplaceAll, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, regexReplaceAll, 2, "String or Real")
|
|
}
|
|
|
|
repl, err := ifaceToString(arguments[2])
|
|
if err != nil {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, regexReplaceAll, 3, "String or Real")
|
|
}
|
|
|
|
reg, err := regexp.Compile(regex.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf(genericError, regexReplaceAll, err.Error())
|
|
}
|
|
return string(reg.ReplaceAll([]byte(src), []byte(repl))), nil
|
|
}
|
|
|
|
func jpRegexReplaceAllLiteral(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexReplaceAllLiteral, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, regexReplaceAllLiteral, 2, "String or Real")
|
|
}
|
|
|
|
repl, err := ifaceToString(arguments[2])
|
|
if err != nil {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, regexReplaceAllLiteral, 3, "String or Real")
|
|
}
|
|
|
|
reg, err := regexp.Compile(regex.String())
|
|
if err != nil {
|
|
return nil, fmt.Errorf(genericError, regexReplaceAllLiteral, err.Error())
|
|
}
|
|
return string(reg.ReplaceAllLiteral([]byte(src), []byte(repl))), nil
|
|
}
|
|
|
|
func jpRegexMatch(arguments []interface{}) (interface{}, error) {
|
|
var err error
|
|
regex, err := validateArg(regexMatch, arguments, 0, reflect.String)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := ifaceToString(arguments[1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, regexMatch, 2, "String or Real")
|
|
}
|
|
|
|
return regexp.Match(regex.String(), []byte(src))
|
|
}
|
|
|
|
func jpLabelMatch(arguments []interface{}) (interface{}, error) {
|
|
labelMap, ok := arguments[0].(map[string]interface{})
|
|
|
|
if !ok {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, labelMatch, 0, "Object")
|
|
}
|
|
|
|
matchMap, ok := arguments[1].(map[string]interface{})
|
|
|
|
if !ok {
|
|
return nil, fmt.Errorf(invalidArgumentTypeError, labelMatch, 1, "Object")
|
|
}
|
|
|
|
for key, value := range labelMap {
|
|
if val, ok := matchMap[key]; !ok || val != value {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// InterfaceToString casts an interface to a string type
|
|
func ifaceToString(iface interface{}) (string, error) {
|
|
switch iface.(type) {
|
|
case int:
|
|
return strconv.Itoa(iface.(int)), nil
|
|
case float64:
|
|
return strconv.FormatFloat(iface.(float64), 'f', -1, 32), nil
|
|
case float32:
|
|
return strconv.FormatFloat(iface.(float64), 'f', -1, 32), nil
|
|
case string:
|
|
return iface.(string), nil
|
|
case bool:
|
|
return strconv.FormatBool(iface.(bool)), nil
|
|
default:
|
|
return "", errors.New("error, undefined type cast")
|
|
}
|
|
}
|
|
|
|
func validateArg(f string, arguments []interface{}, index int, expectedType reflect.Kind) (reflect.Value, error) {
|
|
arg := reflect.ValueOf(arguments[index])
|
|
if arg.Type().Kind() != expectedType {
|
|
return reflect.Value{}, fmt.Errorf(invalidArgumentTypeError, equalFold, index+1, expectedType.String())
|
|
}
|
|
|
|
return arg, nil
|
|
}
|