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:
parent
876a216b5f
commit
c3604c1170
6 changed files with 132 additions and 0 deletions
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
7
test/cli/test-mutate/patched-resource.yaml
Normal file
7
test/cli/test-mutate/patched-resource.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: example
|
||||
annotations:
|
||||
key1: "1"
|
||||
key2: "1"
|
|
@ -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 }}
|
||||
|
|
|
@ -98,3 +98,12 @@ spec:
|
|||
image: nginx:latest
|
||||
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: example
|
||||
annotations:
|
||||
key1: "1"
|
||||
key2: "1"
|
||||
notkey: "2"
|
||||
|
|
Loading…
Add table
Reference in a new issue