From 4c726155a2e7137140d459ac6c923e556ba898df Mon Sep 17 00:00:00 2001 From: Cici Huang Date: Wed, 13 Jul 2022 20:19:59 +0000 Subject: [PATCH] Switch to use cel.TypeToExprType(celType) to generate the exprType. --- .../pkg/apiserver/schema/cel/library/lists.go | 32 +++++-- .../forked/celopenapi/model/schemas.go | 11 ++- .../forked/celopenapi/model/schemas_test.go | 12 ++- .../forked/celopenapi/model/types.go | 85 ++++++++----------- .../forked/celopenapi/model/types_test.go | 16 +++- 5 files changed, 88 insertions(+), 68 deletions(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library/lists.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library/lists.go index c0bdb0871ae..2960e16e90a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library/lists.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library/lists.go @@ -103,15 +103,35 @@ var paramA = cel.TypeParamType("A") // CEL typeParams can be used to constraint to a specific trait (e.g. traits.ComparableType) if the 1st operand is the type to constrain. // But the functions we need to constrain are >, not just . -var summableTypes = map[string]*cel.Type{"int": cel.IntType, "uint": cel.UintType, "double": cel.DoubleType, "duration": cel.DurationType} +// Make sure the order of overload set is deterministic +type namedCELType struct { + typeName string + celType *cel.Type +} + +var summableTypes = []namedCELType{ + {typeName: "int", celType: cel.IntType}, + {typeName: "uint", celType: cel.UintType}, + {typeName: "double", celType: cel.DoubleType}, + {typeName: "duration", celType: cel.DurationType}, +} + var zeroValuesOfSummableTypes = map[string]ref.Val{ "int": types.Int(0), "uint": types.Uint(0), "double": types.Double(0.0), "duration": types.Duration{Duration: 0}, } -var comparableTypes = map[string]*cel.Type{"bool": cel.BoolType, "int": cel.IntType, "uint": cel.UintType, "double": cel.DoubleType, - "duration": cel.DurationType, "timestamp": cel.TimestampType, "string": cel.StringType, "bytes": cel.BytesType} +var comparableTypes = []namedCELType{ + {typeName: "int", celType: cel.IntType}, + {typeName: "uint", celType: cel.UintType}, + {typeName: "double", celType: cel.DoubleType}, + {typeName: "bool", celType: cel.BoolType}, + {typeName: "duration", celType: cel.DurationType}, + {typeName: "timestamp", celType: cel.TimestampType}, + {typeName: "string", celType: cel.StringType}, + {typeName: "bytes", celType: cel.BytesType}, +} // WARNING: All library additions or modifications must follow // https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/2876-crd-validation-expression-language#function-library-updates @@ -285,11 +305,11 @@ func lastIndexOf(list ref.Val, item ref.Val) ref.Val { // templatedOverloads returns overloads for each of the provided types. The template function is called with each type // name (map key) and type to construct the overloads. -func templatedOverloads(types map[string]*cel.Type, template func(name string, t *cel.Type) cel.FunctionOpt) []cel.FunctionOpt { +func templatedOverloads(types []namedCELType, template func(name string, t *cel.Type) cel.FunctionOpt) []cel.FunctionOpt { overloads := make([]cel.FunctionOpt, len(types)) i := 0 - for name, t := range types { - overloads[i] = template(name, t) + for _, t := range types { + overloads[i] = template(t.typeName, t.celType) i++ } return overloads diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go index 5eda1293c34..65d7c7682fb 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas.go @@ -18,7 +18,6 @@ import ( "github.com/google/cel-go/cel" "time" - "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common/types" "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" @@ -73,7 +72,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType { // To validate requirements on both the int and string representation: // `type(intOrStringField) == int ? intOrStringField < 5 : double(intOrStringField.replace('%', '')) < 0.5 // - dyn := newSimpleType("dyn", decls.Dyn, cel.DynType, nil) + dyn := newSimpleType("dyn", cel.DynType, nil) // handle x-kubernetes-int-or-string by returning the max length of the largest possible string dyn.MaxElements = maxRequestSizeBytes - 2 return dyn @@ -150,7 +149,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType { if s.ValueValidation != nil { switch s.ValueValidation.Format { case "byte": - byteWithMaxLength := newSimpleType("bytes", decls.Bytes, cel.BytesType, types.Bytes([]byte{})) + byteWithMaxLength := newSimpleType("bytes", cel.BytesType, types.Bytes([]byte{})) if s.ValueValidation.MaxLength != nil { byteWithMaxLength.MaxElements = zeroIfNegative(*s.ValueValidation.MaxLength) } else { @@ -158,16 +157,16 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType { } return byteWithMaxLength case "duration": - durationWithMaxLength := newSimpleType("duration", decls.Duration, cel.DurationType, types.Duration{Duration: time.Duration(0)}) + durationWithMaxLength := newSimpleType("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}) durationWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s) return durationWithMaxLength case "date", "date-time": - timestampWithMaxLength := newSimpleType("timestamp", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}}) + timestampWithMaxLength := newSimpleType("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}) timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s) return timestampWithMaxLength } } - strWithMaxLength := newSimpleType("string", decls.String, cel.StringType, types.String("")) + strWithMaxLength := newSimpleType("string", cel.StringType, types.String("")) if s.ValueValidation != nil && s.ValueValidation.MaxLength != nil { // multiply the user-provided max length by 4 in the case of an otherwise-untyped string // we do this because the OpenAPIv3 spec indicates that maxLength is specified in runes/code points, diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go index 2490911a785..6aff03d2e3d 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/schemas_test.go @@ -107,8 +107,16 @@ func TestSchemaDeclTypes(t *testing.T) { t.Errorf("missing type in rule types: %s", exp) continue } - if !proto.Equal(expType.ExprType(), actType.ExprType()) { - t.Errorf("incompatible CEL types. got=%v, wanted=%v", actType.ExprType(), expType.ExprType()) + expT, err := expType.ExprType() + if err != nil { + t.Errorf("fail to get cel type: %s", err) + } + actT, err := actType.ExprType() + if err != nil { + t.Errorf("fail to get cel type: %s", err) + } + if !proto.Equal(expT, actT) { + t.Errorf("incompatible CEL types. got=%v, wanted=%v", expT, actT) } } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go index 6ef0488c11f..f5eb7da976a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types.go @@ -20,7 +20,6 @@ import ( "time" "github.com/google/cel-go/cel" - "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/traits" @@ -41,7 +40,6 @@ func NewListType(elem *DeclType, maxItems int64) *DeclType { name: "list", ElemType: elem, MaxElements: maxItems, - exprType: decls.NewListType(elem.ExprType()), celType: cel.ListType(elem.CelType()), defaultValue: NewListValue(), } @@ -54,7 +52,6 @@ func NewMapType(key, elem *DeclType, maxProperties int64) *DeclType { KeyType: key, ElemType: elem, MaxElements: maxProperties, - exprType: decls.NewMapType(key.ExprType(), elem.ExprType()), celType: cel.MapType(key.CelType(), elem.CelType()), defaultValue: NewMapValue(), } @@ -65,7 +62,6 @@ func NewObjectType(name string, fields map[string]*DeclField) *DeclType { t := &DeclType{ name: name, Fields: fields, - exprType: decls.NewObjectType(name), celType: cel.ObjectType(name), traitMask: traits.FieldTesterType | traits.IndexerType, } @@ -73,34 +69,9 @@ func NewObjectType(name string, fields map[string]*DeclField) *DeclType { return t } -// NewObjectTypeRef returns a reference to an object type by name -func NewObjectTypeRef(name string) *DeclType { - t := &DeclType{ - name: name, - exprType: decls.NewObjectType(name), - celType: cel.ObjectType(name), - traitMask: traits.FieldTesterType | traits.IndexerType, - } - return t -} - -// NewTypeParam creates a type parameter type with a simple name. -// -// Type parameters are resolved at compilation time to concrete types, or CEL 'dyn' type if no -// type assignment can be inferred. -func NewTypeParam(name string) *DeclType { - return &DeclType{ - name: name, - TypeParam: true, - exprType: decls.NewTypeParamType(name), - celType: cel.TypeParamType(name), - } -} - -func newSimpleType(name string, exprType *exprpb.Type, celType *cel.Type, zeroVal ref.Val) *DeclType { +func newSimpleType(name string, celType *cel.Type, zeroVal ref.Val) *DeclType { return &DeclType{ name: name, - exprType: exprType, celType: celType, defaultValue: zeroVal, } @@ -118,7 +89,6 @@ type DeclType struct { Metadata map[string]string MaxElements int64 - exprType *exprpb.Type celType *cel.Type traitMask int defaultValue ref.Val @@ -164,7 +134,6 @@ func (t *DeclType) MaybeAssignTypeName(name string) *DeclType { ElemType: t.ElemType, TypeParam: t.TypeParam, Metadata: t.Metadata, - exprType: decls.NewObjectType(name), celType: cel.ObjectType(name), traitMask: t.traitMask, defaultValue: t.defaultValue, @@ -190,8 +159,8 @@ func (t *DeclType) MaybeAssignTypeName(name string) *DeclType { } // ExprType returns the CEL expression type of this declaration. -func (t *DeclType) ExprType() *exprpb.Type { - return t.exprType +func (t *DeclType) ExprType() (*exprpb.Type, error) { + return cel.TypeToExprType(t.celType) } // CelType returns the CEL type of this declaration. @@ -382,7 +351,11 @@ func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) { } for name, declType := range rt.ruleSchemaDeclTypes.types { tpType, found := tp.FindType(name) - if found && !proto.Equal(tpType, declType.ExprType()) { + expT, err := declType.ExprType() + if err != nil { + return nil, fmt.Errorf("fail to get cel type: %s", err) + } + if found && !proto.Equal(tpType, expT) { return nil, fmt.Errorf( "type %s definition differs between CEL environment and rule", name) } @@ -407,7 +380,11 @@ func (rt *RuleTypes) FindType(typeName string) (*exprpb.Type, bool) { } declType, found := rt.findDeclType(typeName) if found { - return declType.ExprType(), found + expT, err := declType.ExprType() + if err != nil { + return expT, false + } + return expT, found } return rt.TypeProvider.FindType(typeName) } @@ -435,15 +412,23 @@ func (rt *RuleTypes) FindFieldType(typeName, fieldName string) (*ref.FieldType, f, found := st.Fields[fieldName] if found { ft := f.Type + expT, err := ft.ExprType() + if err != nil { + return nil, false + } return &ref.FieldType{ - Type: ft.ExprType(), + Type: expT, }, true } // This could be a dynamic map. if st.IsMap() { et := st.ElemType + expT, err := et.ExprType() + if err != nil { + return nil, false + } return &ref.FieldType{ - Type: et.ExprType(), + Type: expT, }, true } return nil, false @@ -528,42 +513,42 @@ type schemaTypeProvider struct { var ( // AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the // types supported. - AnyType = newSimpleType("any", decls.Any, cel.AnyType, nil) + AnyType = newSimpleType("any", cel.AnyType, nil) // BoolType is equivalent to the CEL 'bool' type. - BoolType = newSimpleType("bool", decls.Bool, cel.BoolType, types.False) + BoolType = newSimpleType("bool", cel.BoolType, types.False) // BytesType is equivalent to the CEL 'bytes' type. - BytesType = newSimpleType("bytes", decls.Bytes, cel.BytesType, types.Bytes([]byte{})) + BytesType = newSimpleType("bytes", cel.BytesType, types.Bytes([]byte{})) // DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value. - DoubleType = newSimpleType("double", decls.Double, cel.DoubleType, types.Double(0)) + DoubleType = newSimpleType("double", cel.DoubleType, types.Double(0)) // DurationType is equivalent to the CEL 'duration' type. - DurationType = newSimpleType("duration", decls.Duration, cel.DurationType, types.Duration{Duration: time.Duration(0)}) + DurationType = newSimpleType("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}) // DateType is equivalent to the CEL 'date' type. - DateType = newSimpleType("date", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}}) + DateType = newSimpleType("date", cel.TimestampType, types.Timestamp{Time: time.Time{}}) // DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be // determined at runtime rather than compile time. - DynType = newSimpleType("dyn", decls.Dyn, cel.DynType, nil) + DynType = newSimpleType("dyn", cel.DynType, nil) // IntType is equivalent to the CEL 'int' type which is a 64-bit signed int. - IntType = newSimpleType("int", decls.Int, cel.IntType, types.IntZero) + IntType = newSimpleType("int", cel.IntType, types.IntZero) // NullType is equivalent to the CEL 'null_type'. - NullType = newSimpleType("null_type", decls.Null, cel.NullType, types.NullValue) + NullType = newSimpleType("null_type", cel.NullType, types.NullValue) // StringType is equivalent to the CEL 'string' type which is expected to be a UTF-8 string. // StringType values may either be string literals or expression strings. - StringType = newSimpleType("string", decls.String, cel.StringType, types.String("")) + StringType = newSimpleType("string", cel.StringType, types.String("")) // TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL. - TimestampType = newSimpleType("timestamp", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}}) + TimestampType = newSimpleType("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}) // UintType is equivalent to the CEL 'uint' type. - UintType = newSimpleType("uint", decls.Uint, cel.UintType, types.Uint(0)) + UintType = newSimpleType("uint", cel.UintType, types.Uint(0)) // ListType is equivalent to the CEL 'list' type. ListType = NewListType(AnyType, noMaxLength) diff --git a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go index 66756bfda95..207b4befa3d 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model/types_test.go @@ -37,8 +37,12 @@ func TestTypes_ListType(t *testing.T) { if list.ElemType.TypeName() != "string" { t.Errorf("got %s, wanted elem type of string", list.ElemType.TypeName()) } - if list.ExprType().GetListType() == nil { - t.Errorf("got %v, wanted CEL list type", list.ExprType()) + expT, err := list.ExprType() + if err != nil { + t.Errorf("fail to get cel type: %s", err) + } + if expT.GetListType() == nil { + t.Errorf("got %v, wanted CEL list type", expT) } } @@ -59,8 +63,12 @@ func TestTypes_MapType(t *testing.T) { if mp.ElemType.TypeName() != "int" { t.Errorf("got %s, wanted elem type of int", mp.ElemType.TypeName()) } - if mp.ExprType().GetMapType() == nil { - t.Errorf("got %v, wanted CEL map type", mp.ExprType()) + expT, err := mp.ExprType() + if err != nil { + t.Errorf("fail to get cel type: %s", err) + } + if expT.GetMapType() == nil { + t.Errorf("got %v, wanted CEL map type", expT) } }