1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Add JMESPath Function items (#3777)

Co-authored-by: Jim Bugwadia <jim@nirmata.com>
Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com>
Co-authored-by: Sambhav Kothari <skothari44@bloomberg.net>
This commit is contained in:
gsweene2 2022-05-04 06:33:24 -04:00 committed by GitHub
parent fca068d0f6
commit af51ceb4ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 132 additions and 3 deletions

View file

@ -8,6 +8,7 @@ import (
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
@ -62,6 +63,7 @@ var (
semverCompare = "semver_compare"
parseJson = "parse_json"
parseYAML = "parse_yaml"
items = "items"
)
const errorPrefix = "JMESPath function '%s': "
@ -368,6 +370,18 @@ func GetFunctions() []*FunctionEntry {
ReturnType: []JpType{JpAny},
Note: "decodes a valid YAML encoded string to the appropriate type provided it can be represented as JSON",
},
{
Entry: &gojmespath.FunctionEntry{Name: items,
Arguments: []ArgSpec{
{Types: []JpType{JpObject}},
{Types: []JpType{JpString}},
{Types: []JpType{JpString}},
},
Handler: jpItems,
},
ReturnType: []JpType{JpArray},
Note: "converts a map to an array of objects where each key:value is an item in the array",
},
}
}
@ -795,6 +809,41 @@ func jpParseYAML(arguments []interface{}) (interface{}, error) {
return output, err
}
func jpItems(arguments []interface{}) (interface{}, error) {
input, ok := arguments[0].(map[string]interface{})
if !ok {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 0, "Object")
}
keyName, ok := arguments[1].(string)
if !ok {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 1, "String")
}
valName, ok := arguments[2].(string)
if !ok {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 2, "String")
}
arrayOfObj := make([]map[string]interface{}, 0)
keys := []string{}
// Sort the keys so that the output is deterministic
for key := range input {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
m := make(map[string]interface{})
m[keyName] = key
m[valName] = input[key]
arrayOfObj = append(arrayOfObj, m)
}
return arrayOfObj, nil
}
// InterfaceToString casts an interface to a string type
func ifaceToString(iface interface{}) (string, error) {
switch i := iface.(type) {

View file

@ -1284,3 +1284,53 @@ func Test_SemverCompare(t *testing.T) {
})
}
}
func Test_Items(t *testing.T) {
testCases := []struct {
object string
keyName string
valName string
expectedResult string
}{
{
object: `{ "key1": "value1" }`,
keyName: `"key"`,
valName: `"value"`,
expectedResult: `[{ "key": "key1", "value": "value1" }]`,
},
{
object: `{ "key1": "value1", "key2": "value2" }`,
keyName: `"key"`,
valName: `"value"`,
expectedResult: `[{ "key": "key1", "value": "value1" }, { "key": "key2", "value": "value2" }]`,
},
{
object: `{ "key1": "value1", "key2": "value2" }`,
keyName: `"myKey"`,
valName: `"myValue"`,
expectedResult: `[{ "myKey": "key1", "myValue": "value1" }, { "myKey": "key2", "myValue": "value2" }]`,
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
query, err := New("items(`" + tc.object + "`,`" + tc.keyName + "`,`" + tc.valName + "`)")
assert.NilError(t, err)
res, err := query.Search("")
assert.NilError(t, err)
result, ok := res.([]map[string]interface{})
assert.Assert(t, ok)
var resource []map[string]interface{}
err = json.Unmarshal([]byte(tc.expectedResult), &resource)
assert.NilError(t, err)
assert.DeepEqual(t, result, resource)
})
}
}

View file

@ -49,3 +49,8 @@ results:
resource: example
kind: Pod
result: pass
- policy: example
rule: items
resource: example
kind: Pod
result: pass

View file

@ -163,3 +163,28 @@ spec:
- key: "A=*"
operator: AnyNotIn
value: "{{ obj }}"
- name: items
context:
- name: obj
variable:
value:
a: 1
b: 2
jmesPath: items(@, 'key', 'value')
- name: expected
variable:
value:
- key: a
value: 1
- key: b
value: 2
match:
resources:
kinds:
- Pod
validate:
deny:
conditions:
- key: "{{ obj }}"
operator: NotEqual
value: "{{ expected }}"