k8s.io/apiserver@v0.31.1/pkg/cel/types.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cel 18 19 import ( 20 "fmt" 21 "math" 22 "time" 23 24 "github.com/google/cel-go/cel" 25 "github.com/google/cel-go/common/types" 26 "github.com/google/cel-go/common/types/ref" 27 "github.com/google/cel-go/common/types/traits" 28 29 exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" 30 "k8s.io/apimachinery/pkg/api/resource" 31 ) 32 33 const ( 34 noMaxLength = math.MaxInt 35 ) 36 37 // NewListType returns a parameterized list type with a specified element type. 38 func NewListType(elem *DeclType, maxItems int64) *DeclType { 39 return &DeclType{ 40 name: "list", 41 ElemType: elem, 42 MaxElements: maxItems, 43 celType: cel.ListType(elem.CelType()), 44 defaultValue: NewListValue(), 45 // a list can always be represented as [] in JSON, so hardcode the min size 46 // to 2 47 MinSerializedSize: 2, 48 } 49 } 50 51 // NewMapType returns a parameterized map type with the given key and element types. 52 func NewMapType(key, elem *DeclType, maxProperties int64) *DeclType { 53 return &DeclType{ 54 name: "map", 55 KeyType: key, 56 ElemType: elem, 57 MaxElements: maxProperties, 58 celType: cel.MapType(key.CelType(), elem.CelType()), 59 defaultValue: NewMapValue(), 60 // a map can always be represented as {} in JSON, so hardcode the min size 61 // to 2 62 MinSerializedSize: 2, 63 } 64 } 65 66 // NewObjectType creates an object type with a qualified name and a set of field declarations. 67 func NewObjectType(name string, fields map[string]*DeclField) *DeclType { 68 t := &DeclType{ 69 name: name, 70 Fields: fields, 71 celType: cel.ObjectType(name), 72 traitMask: traits.FieldTesterType | traits.IndexerType, 73 // an object could potentially be larger than the min size we default to here ({}), 74 // but we rely upon the caller to change MinSerializedSize accordingly if they add 75 // properties to the object 76 MinSerializedSize: 2, 77 } 78 t.defaultValue = NewObjectValue(t) 79 return t 80 } 81 82 func NewSimpleTypeWithMinSize(name string, celType *cel.Type, zeroVal ref.Val, minSize int64) *DeclType { 83 return &DeclType{ 84 name: name, 85 celType: celType, 86 defaultValue: zeroVal, 87 MinSerializedSize: minSize, 88 } 89 } 90 91 // DeclType represents the universal type descriptor for OpenAPIv3 types. 92 type DeclType struct { 93 fmt.Stringer 94 95 name string 96 // Fields contains a map of escaped CEL identifier field names to field declarations. 97 Fields map[string]*DeclField 98 KeyType *DeclType 99 ElemType *DeclType 100 TypeParam bool 101 Metadata map[string]string 102 MaxElements int64 103 // MinSerializedSize represents the smallest possible size in bytes that 104 // the DeclType could be serialized to in JSON. 105 MinSerializedSize int64 106 107 celType *cel.Type 108 traitMask int 109 defaultValue ref.Val 110 } 111 112 // MaybeAssignTypeName attempts to set the DeclType name to a fully qualified name, if the type 113 // is of `object` type. 114 // 115 // The DeclType must return true for `IsObject` or this assignment will error. 116 func (t *DeclType) MaybeAssignTypeName(name string) *DeclType { 117 if t.IsObject() { 118 objUpdated := false 119 if t.name != "object" { 120 name = t.name 121 } else { 122 objUpdated = true 123 } 124 fieldMap := make(map[string]*DeclField, len(t.Fields)) 125 for fieldName, field := range t.Fields { 126 fieldType := field.Type 127 fieldTypeName := fmt.Sprintf("%s.%s", name, fieldName) 128 updated := fieldType.MaybeAssignTypeName(fieldTypeName) 129 if updated == fieldType { 130 fieldMap[fieldName] = field 131 continue 132 } 133 objUpdated = true 134 fieldMap[fieldName] = &DeclField{ 135 Name: fieldName, 136 Type: updated, 137 Required: field.Required, 138 enumValues: field.enumValues, 139 defaultValue: field.defaultValue, 140 } 141 } 142 if !objUpdated { 143 return t 144 } 145 return &DeclType{ 146 name: name, 147 Fields: fieldMap, 148 KeyType: t.KeyType, 149 ElemType: t.ElemType, 150 TypeParam: t.TypeParam, 151 Metadata: t.Metadata, 152 celType: cel.ObjectType(name), 153 traitMask: t.traitMask, 154 defaultValue: t.defaultValue, 155 MinSerializedSize: t.MinSerializedSize, 156 } 157 } 158 if t.IsMap() { 159 elemTypeName := fmt.Sprintf("%s.@elem", name) 160 updated := t.ElemType.MaybeAssignTypeName(elemTypeName) 161 if updated == t.ElemType { 162 return t 163 } 164 return NewMapType(t.KeyType, updated, t.MaxElements) 165 } 166 if t.IsList() { 167 elemTypeName := fmt.Sprintf("%s.@idx", name) 168 updated := t.ElemType.MaybeAssignTypeName(elemTypeName) 169 if updated == t.ElemType { 170 return t 171 } 172 return NewListType(updated, t.MaxElements) 173 } 174 return t 175 } 176 177 // ExprType returns the CEL expression type of this declaration. 178 func (t *DeclType) ExprType() (*exprpb.Type, error) { 179 return cel.TypeToExprType(t.celType) 180 } 181 182 // CelType returns the CEL type of this declaration. 183 func (t *DeclType) CelType() *cel.Type { 184 return t.celType 185 } 186 187 // FindField returns the DeclField with the given name if present. 188 func (t *DeclType) FindField(name string) (*DeclField, bool) { 189 f, found := t.Fields[name] 190 return f, found 191 } 192 193 // HasTrait implements the CEL ref.Type interface making this type declaration suitable for use 194 // within the CEL evaluator. 195 func (t *DeclType) HasTrait(trait int) bool { 196 if t.traitMask&trait == trait { 197 return true 198 } 199 if t.defaultValue == nil { 200 return false 201 } 202 _, isDecl := t.defaultValue.Type().(*DeclType) 203 if isDecl { 204 return false 205 } 206 return t.defaultValue.Type().HasTrait(trait) 207 } 208 209 // IsList returns whether the declaration is a `list` type which defines a parameterized element 210 // type, but not a parameterized key type or fields. 211 func (t *DeclType) IsList() bool { 212 return t.KeyType == nil && t.ElemType != nil && t.Fields == nil 213 } 214 215 // IsMap returns whether the declaration is a 'map' type which defines parameterized key and 216 // element types, but not fields. 217 func (t *DeclType) IsMap() bool { 218 return t.KeyType != nil && t.ElemType != nil && t.Fields == nil 219 } 220 221 // IsObject returns whether the declartion is an 'object' type which defined a set of typed fields. 222 func (t *DeclType) IsObject() bool { 223 return t.KeyType == nil && t.ElemType == nil && t.Fields != nil 224 } 225 226 // String implements the fmt.Stringer interface method. 227 func (t *DeclType) String() string { 228 return t.name 229 } 230 231 // TypeName returns the fully qualified type name for the DeclType. 232 func (t *DeclType) TypeName() string { 233 return t.name 234 } 235 236 // DefaultValue returns the CEL ref.Val representing the default value for this object type, 237 // if one exists. 238 func (t *DeclType) DefaultValue() ref.Val { 239 return t.defaultValue 240 } 241 242 // FieldTypeMap constructs a map of the field and object types nested within a given type. 243 func FieldTypeMap(path string, t *DeclType) map[string]*DeclType { 244 if t.IsObject() && t.TypeName() != "object" { 245 path = t.TypeName() 246 } 247 types := make(map[string]*DeclType) 248 buildDeclTypes(path, t, types) 249 return types 250 } 251 252 func buildDeclTypes(path string, t *DeclType, types map[string]*DeclType) { 253 // Ensure object types are properly named according to where they appear in the schema. 254 if t.IsObject() { 255 // Hack to ensure that names are uniquely qualified and work well with the type 256 // resolution steps which require fully qualified type names for field resolution 257 // to function properly. 258 types[t.TypeName()] = t 259 for name, field := range t.Fields { 260 fieldPath := fmt.Sprintf("%s.%s", path, name) 261 buildDeclTypes(fieldPath, field.Type, types) 262 } 263 } 264 // Map element properties to type names if needed. 265 if t.IsMap() { 266 mapElemPath := fmt.Sprintf("%s.@elem", path) 267 buildDeclTypes(mapElemPath, t.ElemType, types) 268 types[path] = t 269 } 270 // List element properties. 271 if t.IsList() { 272 listIdxPath := fmt.Sprintf("%s.@idx", path) 273 buildDeclTypes(listIdxPath, t.ElemType, types) 274 types[path] = t 275 } 276 } 277 278 // DeclField describes the name, ordinal, and optionality of a field declaration within a type. 279 type DeclField struct { 280 Name string 281 Type *DeclType 282 Required bool 283 enumValues []interface{} 284 defaultValue interface{} 285 } 286 287 func NewDeclField(name string, declType *DeclType, required bool, enumValues []interface{}, defaultValue interface{}) *DeclField { 288 return &DeclField{ 289 Name: name, 290 Type: declType, 291 Required: required, 292 enumValues: enumValues, 293 defaultValue: defaultValue, 294 } 295 } 296 297 // TypeName returns the string type name of the field. 298 func (f *DeclField) TypeName() string { 299 return f.Type.TypeName() 300 } 301 302 // DefaultValue returns the zero value associated with the field. 303 func (f *DeclField) DefaultValue() ref.Val { 304 if f.defaultValue != nil { 305 return types.DefaultTypeAdapter.NativeToValue(f.defaultValue) 306 } 307 return f.Type.DefaultValue() 308 } 309 310 // EnumValues returns the set of values that this field may take. 311 func (f *DeclField) EnumValues() []ref.Val { 312 if f.enumValues == nil || len(f.enumValues) == 0 { 313 return []ref.Val{} 314 } 315 ev := make([]ref.Val, len(f.enumValues)) 316 for i, e := range f.enumValues { 317 ev[i] = types.DefaultTypeAdapter.NativeToValue(e) 318 } 319 return ev 320 } 321 322 func allTypesForDecl(declTypes []*DeclType) map[string]*DeclType { 323 if declTypes == nil { 324 return nil 325 } 326 allTypes := map[string]*DeclType{} 327 for _, declType := range declTypes { 328 for k, t := range FieldTypeMap(declType.TypeName(), declType) { 329 allTypes[k] = t 330 } 331 } 332 333 return allTypes 334 } 335 336 // NewDeclTypeProvider returns an Open API Schema-based type-system which is CEL compatible. 337 func NewDeclTypeProvider(rootTypes ...*DeclType) *DeclTypeProvider { 338 // Note, if the schema indicates that it's actually based on another proto 339 // then prefer the proto definition. For expressions in the proto, a new field 340 // annotation will be needed to indicate the expected environment and type of 341 // the expression. 342 allTypes := allTypesForDecl(rootTypes) 343 return &DeclTypeProvider{ 344 registeredTypes: allTypes, 345 } 346 } 347 348 // DeclTypeProvider extends the CEL ref.TypeProvider interface and provides an Open API Schema-based 349 // type-system. 350 type DeclTypeProvider struct { 351 registeredTypes map[string]*DeclType 352 typeProvider types.Provider 353 typeAdapter types.Adapter 354 recognizeKeywordAsFieldName bool 355 } 356 357 func (rt *DeclTypeProvider) SetRecognizeKeywordAsFieldName(recognize bool) { 358 rt.recognizeKeywordAsFieldName = recognize 359 } 360 361 func (rt *DeclTypeProvider) EnumValue(enumName string) ref.Val { 362 return rt.typeProvider.EnumValue(enumName) 363 } 364 365 func (rt *DeclTypeProvider) FindIdent(identName string) (ref.Val, bool) { 366 return rt.typeProvider.FindIdent(identName) 367 } 368 369 // EnvOptions returns a set of cel.EnvOption values which includes the declaration set 370 // as well as a custom ref.TypeProvider. 371 // 372 // If the DeclTypeProvider value is nil, an empty []cel.EnvOption set is returned. 373 func (rt *DeclTypeProvider) EnvOptions(tp types.Provider) ([]cel.EnvOption, error) { 374 if rt == nil { 375 return []cel.EnvOption{}, nil 376 } 377 rtWithTypes, err := rt.WithTypeProvider(tp) 378 if err != nil { 379 return nil, err 380 } 381 return []cel.EnvOption{ 382 cel.CustomTypeProvider(rtWithTypes), 383 cel.CustomTypeAdapter(rtWithTypes), 384 }, nil 385 } 386 387 // WithTypeProvider returns a new DeclTypeProvider that sets the given TypeProvider 388 // If the original DeclTypeProvider is nil, the returned DeclTypeProvider is still nil. 389 func (rt *DeclTypeProvider) WithTypeProvider(tp types.Provider) (*DeclTypeProvider, error) { 390 if rt == nil { 391 return nil, nil 392 } 393 var ta types.Adapter = types.DefaultTypeAdapter 394 tpa, ok := tp.(types.Adapter) 395 if ok { 396 ta = tpa 397 } 398 rtWithTypes := &DeclTypeProvider{ 399 typeProvider: tp, 400 typeAdapter: ta, 401 registeredTypes: rt.registeredTypes, 402 recognizeKeywordAsFieldName: rt.recognizeKeywordAsFieldName, 403 } 404 for name, declType := range rt.registeredTypes { 405 tpType, found := tp.FindStructType(name) 406 // cast celType to types.type 407 408 expT := declType.CelType() 409 if found && !expT.IsExactType(tpType) { 410 return nil, fmt.Errorf( 411 "type %s definition differs between CEL environment and type provider", name) 412 } 413 414 } 415 return rtWithTypes, nil 416 } 417 418 // FindStructType attempts to resolve the typeName provided from the rule's rule-schema, or if not 419 // from the embedded ref.TypeProvider. 420 // 421 // FindStructType overrides the default type-finding behavior of the embedded TypeProvider. 422 // 423 // Note, when the type name is based on the Open API Schema, the name will reflect the object path 424 // where the type definition appears. 425 func (rt *DeclTypeProvider) FindStructType(typeName string) (*types.Type, bool) { 426 if rt == nil { 427 return nil, false 428 } 429 declType, found := rt.findDeclType(typeName) 430 if found { 431 expT := declType.CelType() 432 return expT, found 433 } 434 return rt.typeProvider.FindStructType(typeName) 435 } 436 437 // FindDeclType returns the CPT type description which can be mapped to a CEL type. 438 func (rt *DeclTypeProvider) FindDeclType(typeName string) (*DeclType, bool) { 439 if rt == nil { 440 return nil, false 441 } 442 return rt.findDeclType(typeName) 443 } 444 445 // FindStructFieldNames returns the field names associated with the type, if the type 446 // is found. 447 func (rt *DeclTypeProvider) FindStructFieldNames(typeName string) ([]string, bool) { 448 return []string{}, false 449 } 450 451 // FindStructFieldType returns a field type given a type name and field name, if found. 452 // 453 // Note, the type name for an Open API Schema type is likely to be its qualified object path. 454 // If, in the future an object instance rather than a type name were provided, the field 455 // resolution might more accurately reflect the expected type model. However, in this case 456 // concessions were made to align with the existing CEL interfaces. 457 func (rt *DeclTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) { 458 st, found := rt.findDeclType(typeName) 459 if !found { 460 return rt.typeProvider.FindStructFieldType(typeName, fieldName) 461 } 462 463 f, found := st.Fields[fieldName] 464 if rt.recognizeKeywordAsFieldName && !found && celReservedSymbols.Has(fieldName) { 465 f, found = st.Fields["__"+fieldName+"__"] 466 } 467 468 if found { 469 ft := f.Type 470 expT := ft.CelType() 471 return &types.FieldType{ 472 Type: expT, 473 }, true 474 } 475 // This could be a dynamic map. 476 if st.IsMap() { 477 et := st.ElemType 478 expT := et.CelType() 479 return &types.FieldType{ 480 Type: expT, 481 }, true 482 } 483 return nil, false 484 } 485 486 // NativeToValue is an implementation of the ref.TypeAdapater interface which supports conversion 487 // of rule values to CEL ref.Val instances. 488 func (rt *DeclTypeProvider) NativeToValue(val interface{}) ref.Val { 489 return rt.typeAdapter.NativeToValue(val) 490 } 491 492 func (rt *DeclTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val { 493 // TODO: implement for OpenAPI types to enable CEL object instantiation, which is needed 494 // for mutating admission. 495 return rt.typeProvider.NewValue(typeName, fields) 496 } 497 498 // TypeNames returns the list of type names declared within the DeclTypeProvider object. 499 func (rt *DeclTypeProvider) TypeNames() []string { 500 typeNames := make([]string, len(rt.registeredTypes)) 501 i := 0 502 for name := range rt.registeredTypes { 503 typeNames[i] = name 504 i++ 505 } 506 return typeNames 507 } 508 509 func (rt *DeclTypeProvider) findDeclType(typeName string) (*DeclType, bool) { 510 declType, found := rt.registeredTypes[typeName] 511 if found { 512 return declType, true 513 } 514 declType = findScalar(typeName) 515 return declType, declType != nil 516 } 517 518 func findScalar(typename string) *DeclType { 519 switch typename { 520 case BoolType.TypeName(): 521 return BoolType 522 case BytesType.TypeName(): 523 return BytesType 524 case DoubleType.TypeName(): 525 return DoubleType 526 case DurationType.TypeName(): 527 return DurationType 528 case IntType.TypeName(): 529 return IntType 530 case NullType.TypeName(): 531 return NullType 532 case StringType.TypeName(): 533 return StringType 534 case TimestampType.TypeName(): 535 return TimestampType 536 case UintType.TypeName(): 537 return UintType 538 case ListType.TypeName(): 539 return ListType 540 case MapType.TypeName(): 541 return MapType 542 default: 543 return nil 544 } 545 } 546 547 var ( 548 // AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the 549 // types supported. 550 AnyType = NewSimpleTypeWithMinSize("any", cel.AnyType, nil, 1) 551 552 // BoolType is equivalent to the CEL 'bool' type. 553 BoolType = NewSimpleTypeWithMinSize("bool", cel.BoolType, types.False, MinBoolSize) 554 555 // BytesType is equivalent to the CEL 'bytes' type. 556 BytesType = NewSimpleTypeWithMinSize("bytes", cel.BytesType, types.Bytes([]byte{}), MinStringSize) 557 558 // DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value. 559 DoubleType = NewSimpleTypeWithMinSize("double", cel.DoubleType, types.Double(0), MinNumberSize) 560 561 // DurationType is equivalent to the CEL 'duration' type. 562 DurationType = NewSimpleTypeWithMinSize("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}, MinDurationSizeJSON) 563 564 // DateType is equivalent to the CEL 'date' type. 565 DateType = NewSimpleTypeWithMinSize("date", cel.TimestampType, types.Timestamp{Time: time.Time{}}, JSONDateSize) 566 567 // DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be 568 // determined at runtime rather than compile time. 569 DynType = NewSimpleTypeWithMinSize("dyn", cel.DynType, nil, 1) 570 571 // IntType is equivalent to the CEL 'int' type which is a 64-bit signed int. 572 IntType = NewSimpleTypeWithMinSize("int", cel.IntType, types.IntZero, MinNumberSize) 573 574 // NullType is equivalent to the CEL 'null_type'. 575 NullType = NewSimpleTypeWithMinSize("null_type", cel.NullType, types.NullValue, 4) 576 577 // StringType is equivalent to the CEL 'string' type which is expected to be a UTF-8 string. 578 // StringType values may either be string literals or expression strings. 579 StringType = NewSimpleTypeWithMinSize("string", cel.StringType, types.String(""), MinStringSize) 580 581 // TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL. 582 // Note that both the OpenAPI date and date-time types map onto TimestampType, so not all types 583 // labeled as Timestamp will necessarily have the same MinSerializedSize. 584 TimestampType = NewSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, JSONDateSize) 585 586 // QuantityDeclType wraps a [QuantityType] and makes it usable with functions that expect 587 // a [DeclType]. 588 QuantityDeclType = NewSimpleTypeWithMinSize("quantity", QuantityType, Quantity{Quantity: resource.NewQuantity(0, resource.DecimalSI)}, 8) 589 590 // UintType is equivalent to the CEL 'uint' type. 591 UintType = NewSimpleTypeWithMinSize("uint", cel.UintType, types.Uint(0), 1) 592 593 // ListType is equivalent to the CEL 'list' type. 594 ListType = NewListType(AnyType, noMaxLength) 595 596 // MapType is equivalent to the CEL 'map' type. 597 MapType = NewMapType(AnyType, AnyType, noMaxLength) 598 )