Repository: kyverno/kyverno
Pull request #6180 (2023-01-31 14:37:20)

feat: support arrays in jp items() function
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

## Explanation

This PR adds support for arrays in jp items() function.

## Related issue

Closes  https://github.com/kyverno/kyverno/issues/6178


codecov[bot]: # [Codecov](https://codecov.io/gh/kyverno/kyverno/pull/6180?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) Report
> Merging [#6180](https://codecov.io/gh/kyverno/kyverno/pull/6180?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (050cfb4) into [main](https://codecov.io/gh/kyverno/kyverno/commit/848596ca8d8429f21890621763f68777e93343c9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) (848596c) will **increase** coverage by `0.02%`.
> The diff coverage is `93.10%`.

```diff
@@            Coverage Diff             @@
##             main    #6180      +/-   ##
==========================================
+ Coverage   36.97%   36.99%   +0.02%     
==========================================
  Files         196      196              
  Lines       21015    21021       +6     
==========================================
+ Hits         7770     7777       +7     
  Misses      12416    12416              
+ Partials      829      828       -1     
```


| [Impacted Files](https://codecov.io/gh/kyverno/kyverno/pull/6180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno) | Coverage Δ | |
|---|---|---|
| [pkg/engine/jmespath/functions.go](https://codecov.io/gh/kyverno/kyverno/pull/6180?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno#diff-cGtnL2VuZ2luZS9qbWVzcGF0aC9mdW5jdGlvbnMuZ28=) | `77.63% <93.10%> (+0.25%)` | :arrow_up: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kyverno)


Patch:
diff --git a/pkg/engine/jmespath/functions.go b/pkg/engine/jmespath/functions.go
index abb32e0e6..f5141733f 100644
--- a/pkg/engine/jmespath/functions.go
+++ b/pkg/engine/jmespath/functions.go
@@ -450,14 +450,14 @@ func GetFunctions() []*FunctionEntry {
 			Entry: &gojmespath.FunctionEntry{
 				Name: items,
 				Arguments: []ArgSpec{
-					{Types: []JpType{JpObject}},
+					{Types: []JpType{JpObject, JpArray}},
 					{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",
+			Note:       "converts a map or array to an array of objects where each key:value is an item in the array",
 		},
 		{
 			Entry: &gojmespath.FunctionEntry{
@@ -984,10 +984,6 @@ func jpParseYAML(arguments []interface{}) (interface{}, error) {
 }
 
 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")
@@ -996,26 +992,34 @@ func jpItems(arguments []interface{}) (interface{}, error) {
 	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)
+	switch input := arguments[0].(type) {
+	case map[string]interface{}:
+		var keys []string
+		// Sort the keys so that the output is deterministic
+		for key := range input {
+			keys = append(keys, key)
+		}
+		sort.Strings(keys)
+		var arrayOfObj []map[string]interface{}
+		for _, key := range keys {
+			arrayOfObj = append(arrayOfObj, map[string]interface{}{
+				keyName: key,
+				valName: input[key],
+			})
+		}
+		return arrayOfObj, nil
+	case []interface{}:
+		var arrayOfObj []map[string]interface{}
+		for index, value := range input {
+			arrayOfObj = append(arrayOfObj, map[string]interface{}{
+				keyName: float64(index),
+				valName: value,
+			})
+		}
+		return arrayOfObj, nil
+	default:
+		return nil, fmt.Errorf(invalidArgumentTypeError, arguments, 0, "Object")
 	}
-
-	return arrayOfObj, nil
 }
 
 func jpObjectFromLists(arguments []interface{}) (interface{}, error) {
@@ -1068,7 +1072,6 @@ func validateArg(f string, arguments []interface{}, index int, expectedType refl
 	if arg.Type().Kind() != expectedType {
 		return reflect.Value{}, fmt.Errorf(invalidArgumentTypeError, f, index+1, expectedType.String())
 	}
-
 	return arg, nil
 }
 


Test patch:
diff --git a/pkg/engine/jmespath/functions_test.go b/pkg/engine/jmespath/functions_test.go
index 0c6fd4410..4848cee52 100644
--- a/pkg/engine/jmespath/functions_test.go
+++ b/pkg/engine/jmespath/functions_test.go
@@ -1349,6 +1349,12 @@ func Test_Items(t *testing.T) {
 			valName:        `"myValue"`,
 			expectedResult: `[{ "myKey": "key1", "myValue": "value1" }, { "myKey": "key2", "myValue": "value2" }]`,
 		},
+		{
+			object:         `["A", "B", "C"]`,
+			keyName:        `"myKey"`,
+			valName:        `"myValue"`,
+			expectedResult: `[{ "myKey": 0, "myValue": "A" }, { "myKey": 1, "myValue": "B" }, { "myKey": 2, "myValue": "C" }]`,
+		},
 	}
 	for i, tc := range testCases {
 		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {