diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go index 86a9313e410..1b4c3d3e1e4 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go @@ -338,6 +338,21 @@ func TestValidationExpressions(t *testing.T) { "self.val1 + self.val2 == [1, 2, 3, 1, 2, 3]", "self.val1 + [4, 5] == [1, 2, 3, 4, 5]", }, + errors: map[string]string{ + // Mixed type lists are not allowed since we have HomogeneousAggregateLiterals enabled + "[1, 'a', false].filter(x, string(x) == 'a')": "expected type 'int' but found 'string'", + }, + }, + {name: "string lists", + obj: objs([]interface{}{"a", "b", "c"}), + schema: schemas(listType(&stringType)), + valid: []string{ + // Join function + "self.val1.join('-') == 'a-b-c'", + "['a', 'b', 'c'].join('-') == 'a-b-c'", + "self.val1.join() == 'abc'", + "['a', 'b', 'c'].join() == 'abc'", + }, }, {name: "listSets", obj: objs([]interface{}{"a", "b", "c"}, []interface{}{"a", "c", "b"}), @@ -351,6 +366,12 @@ func TestValidationExpressions(t *testing.T) { "!('x' in self.val1)", "self.val1 + self.val2 == ['a', 'b', 'c']", "self.val1 + ['c', 'd'] == ['a', 'b', 'c', 'd']", + + // Join function + "self.val1.join('-') == 'a-b-c'", + "['a', 'b', 'c'].join('-') == 'a-b-c'", + "self.val1.join() == 'abc'", + "['a', 'b', 'c'].join() == 'abc'", }, }, {name: "listMaps", @@ -403,6 +424,10 @@ func TestValidationExpressions(t *testing.T) { "!('k3' in self.val1)", "self.val1 == {'k1': 'a', 'k2': 'b'}", }, + errors: map[string]string{ + // Mixed type maps are not allowed since we have HomogeneousAggregateLiterals enabled + "{'k1': 'a', 'k2': 1, 'k2': false}": "expected type 'string' but found 'int'", + }, }, {name: "objects", obj: map[string]interface{}{ diff --git a/staging/src/k8s.io/apiserver/pkg/cel/common/values.go b/staging/src/k8s.io/apiserver/pkg/cel/common/values.go index e6d7b99757e..9deab4294bb 100644 --- a/staging/src/k8s.io/apiserver/pkg/cel/common/values.go +++ b/staging/src/k8s.io/apiserver/pkg/cel/common/values.go @@ -26,9 +26,10 @@ import ( "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/traits" + "k8s.io/kube-openapi/pkg/validation/strfmt" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apiserver/pkg/cel" - "k8s.io/kube-openapi/pkg/validation/strfmt" ) // UnstructuredToVal converts a Kubernetes unstructured data element to a CEL Val. @@ -425,7 +426,22 @@ var _ = traits.Lister(&unstructuredList{}) func (t *unstructuredList) ConvertToNative(typeDesc reflect.Type) (interface{}, error) { switch typeDesc.Kind() { case reflect.Slice: - return t.elements, nil + switch t.itemsSchema.Type() { + // Workaround for https://github.com/kubernetes/kubernetes/issues/117590 until we + // resolve the desired behavior in cel-go via https://github.com/google/cel-go/issues/688 + case "string": + var result []string + for _, e := range t.elements { + s, ok := e.(string) + if !ok { + return nil, fmt.Errorf("unexpected all elements to be of type string, but got %T", e) + } + result = append(result, s) + } + return result, nil + default: + return t.elements, nil + } } return nil, fmt.Errorf("type conversion error from '%s' to '%s'", t.Type(), typeDesc) }