1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

fix: foreach list validation (#11222)

Signed-off-by: airycanon <airycanon@airycanon.me>
This commit is contained in:
Yukun Wang 2024-09-26 16:49:35 +08:00 committed by GitHub
parent 819d9bf540
commit 7763ff72cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 84 additions and 0 deletions

View file

@ -9,12 +9,22 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
// EvaluateList evaluates the context using the given JMESPath expression and returns a unified slice of interfaces.
func EvaluateList(jmesPath string, ctx enginecontext.EvalInterface) ([]interface{}, error) { func EvaluateList(jmesPath string, ctx enginecontext.EvalInterface) ([]interface{}, error) {
i, err := ctx.Query(jmesPath) i, err := ctx.Query(jmesPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, ok := i.([]map[string]interface{})
if ok {
l := make([]interface{}, 0, len(m))
for _, e := range m {
l = append(l, e)
}
return l, nil
}
l, ok := i.([]interface{}) l, ok := i.([]interface{})
if !ok { if !ok {
return []interface{}{i}, nil return []interface{}{i}, nil

View file

@ -3,6 +3,9 @@ package utils
import ( import (
"testing" "testing"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -14,3 +17,74 @@ func Test_InvertElements(t *testing.T) {
assert.Equal(t, "b", elemsInverted[1]) assert.Equal(t, "b", elemsInverted[1])
assert.Equal(t, "c", elemsInverted[0]) assert.Equal(t, "c", elemsInverted[0])
} }
func Test_EvaluateList(t *testing.T) {
entryName := "test_object"
cases := []struct {
name string
rawData []byte
jmesPath string
expected interface{}
}{
{
name: "slice data",
rawData: []byte(`["test-value-1", "test-value-2"]`),
jmesPath: entryName,
expected: []interface{}{"test-value-1", "test-value-2"},
},
{
name: "map data",
rawData: []byte(`
{
"test-key-1": "test-value-1",
"test-key-2": "test-value-2"
}
`),
jmesPath: entryName + ".items(@, 'key', 'value')",
expected: []interface{}{
map[string]interface{}{
"key": "test-key-1",
"value": "test-value-1",
},
map[string]interface{}{
"key": "test-key-2",
"value": "test-value-2",
},
},
},
{
name: "map data with custom fields",
rawData: []byte(`
{
"test-key-1": "test-value-1",
"test-key-2": "test-value-2"
}
`),
jmesPath: "test_object.items(@, 'another-key', 'another-value')",
expected: []interface{}{
map[string]interface{}{
"another-key": "test-key-1",
"another-value": "test-value-1",
},
map[string]interface{}{
"another-key": "test-key-2",
"another-value": "test-value-2",
},
},
},
}
cfg := config.NewDefaultConfiguration(false)
jp := jmespath.New(cfg)
for _, item := range cases {
t.Run(item.name, func(t *testing.T) {
ctx := context.NewContext(jp)
assert.NoError(t, ctx.AddContextEntry(entryName, item.rawData))
list, err := EvaluateList(item.jmesPath, ctx)
assert.NoError(t, err)
assert.Equal(t, item.expected, list)
})
}
}