1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 18:38:40 +00:00

Add an object_from_lists function (#3824)

This commit is contained in:
Sambhav Kothari 2022-05-07 13:05:04 +01:00 committed by GitHub
parent 876a216b5f
commit c3604c1170
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 0 deletions

View file

@ -64,6 +64,7 @@ var (
parseJson = "parse_json"
parseYAML = "parse_yaml"
items = "items"
objectFromLists = "object_from_lists"
)
const errorPrefix = "JMESPath function '%s': "
@ -382,6 +383,17 @@ func GetFunctions() []*FunctionEntry {
ReturnType: []JpType{JpArray},
Note: "converts a map to an array of objects where each key:value is an item in the array",
},
{
Entry: &gojmespath.FunctionEntry{Name: objectFromLists,
Arguments: []ArgSpec{
{Types: []JpType{JpArray}},
{Types: []JpType{JpArray}},
},
Handler: jpObjectFromLists,
},
ReturnType: []JpType{JpObject},
Note: "converts a pair of lists containing keys and values to an object",
},
}
}
@ -844,6 +856,33 @@ func jpItems(arguments []interface{}) (interface{}, error) {
return arrayOfObj, nil
}
func jpObjectFromLists(arguments []interface{}) (interface{}, error) {
keys, ok := arguments[0].([]interface{})
if !ok {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 0, "Array")
}
values, ok := arguments[1].([]interface{})
if !ok {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 1, "Array")
}
output := map[string]interface{}{}
for i, ikey := range keys {
key, err := ifaceToString(ikey)
if err != nil {
return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 0, "StringArray")
}
if i < len(values) {
output[key] = values[i]
} else {
output[key] = nil
}
}
return output, nil
}
// InterfaceToString casts an interface to a string type
func ifaceToString(iface interface{}) (string, error) {
switch i := iface.(type) {

View file

@ -1334,3 +1334,50 @@ func Test_Items(t *testing.T) {
}
}
func Test_ObjectFromLists(t *testing.T) {
testCases := []struct {
keys string
values string
expectedResult map[string]interface{}
}{
{
keys: `["key1", "key2"]`,
values: `["1", "2"]`,
expectedResult: map[string]interface{}{
"key1": "1",
"key2": "2",
},
},
{
keys: `["key1", "key2"]`,
values: `[1, "2"]`,
expectedResult: map[string]interface{}{
"key1": 1.0,
"key2": "2",
},
},
{
keys: `["key1", "key2"]`,
values: `[1]`,
expectedResult: map[string]interface{}{
"key1": 1.0,
"key2": nil,
},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
query, err := New("object_from_lists(`" + tc.keys + "`,`" + tc.values + "`)")
assert.NilError(t, err)
res, err := query.Search("")
assert.NilError(t, err)
result, ok := res.(map[string]interface{})
assert.Assert(t, ok)
assert.DeepEqual(t, result, tc.expectedResult)
})
}
}

View file

@ -82,3 +82,9 @@ results:
patchedResource: patchedResource11.yaml
kind: Pod
result: skip
- policy: example
rule: object_from_lists
resource: example
patchedResource: patched-resource.yaml
kind: Pod
result: pass

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Pod
metadata:
name: example
annotations:
key1: "1"
key2: "1"

View file

@ -75,3 +75,27 @@ spec:
options:
- name: ndots
value: "1"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: example
spec:
rules:
- name: object_from_lists
context:
- name: annotations
variable:
jmesPath: items(request.object.metadata.annotations, 'key', 'value')[?starts_with(key, 'key')]
- name: annotations
variable:
jmesPath: object_from_lists(annotations[].key, annotations[].value)
match:
resources:
kinds:
- Pod
mutate:
patchesJson6902: |-
- path: "/metadata/annotations"
op: replace
value: {{ annotations }}

View file

@ -98,3 +98,12 @@ spec:
image: nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
name: example
annotations:
key1: "1"
key2: "1"
notkey: "2"