mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #112926 from jiahuif-forks/refactor/cel-out-of-apiextensions
split and move CEL package
This commit is contained in:
commit
61ca612cbb
@ -25,6 +25,7 @@ import (
|
|||||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
"k8s.io/apiserver/pkg/cel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// unbounded uses nil to represent an unbounded cardinality value.
|
// unbounded uses nil to represent an unbounded cardinality value.
|
||||||
@ -109,7 +110,7 @@ type CELTypeInfo struct {
|
|||||||
// Schema is a structural schema for this CELSchemaContext node. It must be non-nil.
|
// Schema is a structural schema for this CELSchemaContext node. It must be non-nil.
|
||||||
Schema *structuralschema.Structural
|
Schema *structuralschema.Structural
|
||||||
// DeclType is a CEL declaration representation of Schema of this CELSchemaContext node. It must be non-nil.
|
// DeclType is a CEL declaration representation of Schema of this CELSchemaContext node. It must be non-nil.
|
||||||
DeclType *model.DeclType
|
DeclType *cel.DeclType
|
||||||
}
|
}
|
||||||
|
|
||||||
// converter converts from JSON schema to a structural schema and a CEL declType, or returns an error if the conversion
|
// converter converts from JSON schema to a structural schema and a CEL declType, or returns an error if the conversion
|
||||||
@ -224,7 +225,7 @@ type propertyTypeInfoAccessor struct {
|
|||||||
func (c propertyTypeInfoAccessor) accessTypeInfo(parentTypeInfo *CELTypeInfo) *CELTypeInfo {
|
func (c propertyTypeInfoAccessor) accessTypeInfo(parentTypeInfo *CELTypeInfo) *CELTypeInfo {
|
||||||
if parentTypeInfo.Schema.Properties != nil {
|
if parentTypeInfo.Schema.Properties != nil {
|
||||||
propSchema := parentTypeInfo.Schema.Properties[c.propertyName]
|
propSchema := parentTypeInfo.Schema.Properties[c.propertyName]
|
||||||
if escapedPropName, ok := model.Escape(c.propertyName); ok {
|
if escapedPropName, ok := cel.Escape(c.propertyName); ok {
|
||||||
if fieldDeclType, ok := parentTypeInfo.DeclType.Fields[escapedPropName]; ok {
|
if fieldDeclType, ok := parentTypeInfo.DeclType.Fields[escapedPropName]; ok {
|
||||||
return &CELTypeInfo{Schema: &propSchema, DeclType: fieldDeclType.Type}
|
return &CELTypeInfo{Schema: &propSchema, DeclType: fieldDeclType.Type}
|
||||||
} // else fields with unknown types are omitted from CEL validation entirely
|
} // else fields with unknown types are omitted from CEL validation entirely
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
@ -1038,7 +1039,7 @@ func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSch
|
|||||||
celContext.TotalCost.ObserveExpressionCost(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), expressionCost)
|
celContext.TotalCost.ObserveExpressionCost(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), expressionCost)
|
||||||
}
|
}
|
||||||
if cr.Error != nil {
|
if cr.Error != nil {
|
||||||
if cr.Error.Type == cel.ErrorTypeRequired {
|
if cr.Error.Type == apiservercel.ErrorTypeRequired {
|
||||||
allErrs.CELErrors = append(allErrs.CELErrors, field.Required(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), cr.Error.Detail))
|
allErrs.CELErrors = append(allErrs.CELErrors, field.Required(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), cr.Error.Detail))
|
||||||
} else {
|
} else {
|
||||||
allErrs.CELErrors = append(allErrs.CELErrors, field.Invalid(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), schema.XValidations[i], cr.Error.Detail))
|
allErrs.CELErrors = append(allErrs.CELErrors, field.Invalid(fldPath.Child("x-kubernetes-validations").Index(i).Child("rule"), schema.XValidations[i], cr.Error.Detail))
|
||||||
|
@ -24,11 +24,13 @@ import (
|
|||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"github.com/google/cel-go/checker"
|
"github.com/google/cel-go/checker"
|
||||||
|
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/metrics"
|
|
||||||
celmodel "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
celmodel "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
|
"k8s.io/apiserver/pkg/cel/library"
|
||||||
|
"k8s.io/apiserver/pkg/cel/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -56,7 +58,7 @@ const (
|
|||||||
// CompilationResult represents the cel compilation result for one rule
|
// CompilationResult represents the cel compilation result for one rule
|
||||||
type CompilationResult struct {
|
type CompilationResult struct {
|
||||||
Program cel.Program
|
Program cel.Program
|
||||||
Error *Error
|
Error *apiservercel.Error
|
||||||
// If true, the compiled expression contains a reference to the identifier "oldSelf", and its corresponding rule
|
// If true, the compiled expression contains a reference to the identifier "oldSelf", and its corresponding rule
|
||||||
// is implicitly a transition rule.
|
// is implicitly a transition rule.
|
||||||
TransitionRule bool
|
TransitionRule bool
|
||||||
@ -97,7 +99,7 @@ func getBaseEnv() (*cel.Env, error) {
|
|||||||
// - nil Program, nil Error: The provided rule was empty so compilation was not attempted
|
// - nil Program, nil Error: The provided rule was empty so compilation was not attempted
|
||||||
//
|
//
|
||||||
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit as input.
|
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit as input.
|
||||||
func Compile(s *schema.Structural, declType *celmodel.DeclType, perCallLimit uint64) ([]CompilationResult, error) {
|
func Compile(s *schema.Structural, declType *apiservercel.DeclType, perCallLimit uint64) ([]CompilationResult, error) {
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
defer metrics.Metrics.ObserveCompilation(time.Since(t))
|
defer metrics.Metrics.ObserveCompilation(time.Since(t))
|
||||||
if len(s.Extensions.XValidations) == 0 {
|
if len(s.Extensions.XValidations) == 0 {
|
||||||
@ -106,15 +108,15 @@ func Compile(s *schema.Structural, declType *celmodel.DeclType, perCallLimit uin
|
|||||||
celRules := s.Extensions.XValidations
|
celRules := s.Extensions.XValidations
|
||||||
|
|
||||||
var propDecls []cel.EnvOption
|
var propDecls []cel.EnvOption
|
||||||
var root *celmodel.DeclType
|
var root *apiservercel.DeclType
|
||||||
var ok bool
|
var ok bool
|
||||||
baseEnv, err := getBaseEnv()
|
baseEnv, err := getBaseEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reg := celmodel.NewRegistry(baseEnv)
|
reg := apiservercel.NewRegistry(baseEnv)
|
||||||
scopedTypeName := generateUniqueSelfTypeName()
|
scopedTypeName := generateUniqueSelfTypeName()
|
||||||
rt, err := celmodel.NewRuleTypes(scopedTypeName, declType, reg)
|
rt, err := apiservercel.NewRuleTypes(scopedTypeName, declType, reg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -158,18 +160,18 @@ func compileRule(rule apiextensions.ValidationRule, env *cel.Env, perCallLimit u
|
|||||||
}
|
}
|
||||||
ast, issues := env.Compile(rule.Rule)
|
ast, issues := env.Compile(rule.Rule)
|
||||||
if issues != nil {
|
if issues != nil {
|
||||||
compilationResult.Error = &Error{ErrorTypeInvalid, "compilation failed: " + issues.String()}
|
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "compilation failed: " + issues.String()}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ast.OutputType() != cel.BoolType {
|
if ast.OutputType() != cel.BoolType {
|
||||||
compilationResult.Error = &Error{ErrorTypeInvalid, "cel expression must evaluate to a bool"}
|
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "cel expression must evaluate to a bool"}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
checkedExpr, err := cel.AstToCheckedExpr(ast)
|
checkedExpr, err := cel.AstToCheckedExpr(ast)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// should be impossible since env.Compile returned no issues
|
// should be impossible since env.Compile returned no issues
|
||||||
compilationResult.Error = &Error{ErrorTypeInternal, "unexpected compilation error: " + err.Error()}
|
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInternal, "unexpected compilation error: " + err.Error()}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, ref := range checkedExpr.ReferenceMap {
|
for _, ref := range checkedExpr.ReferenceMap {
|
||||||
@ -188,12 +190,12 @@ func compileRule(rule apiextensions.ValidationRule, env *cel.Env, perCallLimit u
|
|||||||
cel.InterruptCheckFrequency(checkFrequency),
|
cel.InterruptCheckFrequency(checkFrequency),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
compilationResult.Error = &Error{ErrorTypeInvalid, "program instantiation failed: " + err.Error()}
|
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInvalid, "program instantiation failed: " + err.Error()}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
costEst, err := env.EstimateCost(ast, estimator)
|
costEst, err := env.EstimateCost(ast, estimator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
compilationResult.Error = &Error{ErrorTypeInternal, "cost estimation failed: " + err.Error()}
|
compilationResult.Error = &apiservercel.Error{apiservercel.ErrorTypeInternal, "cost estimation failed: " + err.Error()}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
compilationResult.MaxCost = costEst.Max
|
compilationResult.MaxCost = costEst.Max
|
||||||
@ -210,12 +212,12 @@ func generateUniqueSelfTypeName() string {
|
|||||||
return fmt.Sprintf("selfType%d", time.Now().Nanosecond())
|
return fmt.Sprintf("selfType%d", time.Now().Nanosecond())
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCostEstimator(root *celmodel.DeclType) *library.CostEstimator {
|
func newCostEstimator(root *apiservercel.DeclType) *library.CostEstimator {
|
||||||
return &library.CostEstimator{SizeEstimator: &sizeEstimator{root: root}}
|
return &library.CostEstimator{SizeEstimator: &sizeEstimator{root: root}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type sizeEstimator struct {
|
type sizeEstimator struct {
|
||||||
root *celmodel.DeclType
|
root *apiservercel.DeclType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *sizeEstimator) EstimateSize(element checker.AstNode) *checker.SizeEstimate {
|
func (c *sizeEstimator) EstimateSize(element checker.AstNode) *checker.SizeEstimate {
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/cel"
|
||||||
|
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||||
@ -79,12 +81,12 @@ func (m fnMatcher) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type errorMatcher struct {
|
type errorMatcher struct {
|
||||||
errorType ErrorType
|
errorType cel.ErrorType
|
||||||
contains string
|
contains string
|
||||||
}
|
}
|
||||||
|
|
||||||
func invalidError(contains string) validationMatcher {
|
func invalidError(contains string) validationMatcher {
|
||||||
return errorMatcher{errorType: ErrorTypeInvalid, contains: contains}
|
return errorMatcher{errorType: cel.ErrorTypeInvalid, contains: contains}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v errorMatcher) matches(cr CompilationResult) bool {
|
func (v errorMatcher) matches(cr CompilationResult) bool {
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
This directory contains a port of https://github.com/google/cel-policy-templates-go/tree/master/policy/model modified in a few ways:
|
|
||||||
|
|
||||||
- Uses the Structural schema types
|
|
||||||
- All template related code has been removed
|
|
@ -22,37 +22,16 @@ import (
|
|||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"github.com/google/cel-go/common/types"
|
"github.com/google/cel-go/common/types"
|
||||||
|
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// the largest request that will be accepted is 3MB
|
|
||||||
// TODO(DangerOnTheRanger): wire in MaxRequestBodyBytes from apiserver/pkg/server/options/server_run_options.go to make this configurable
|
// TODO(DangerOnTheRanger): wire in MaxRequestBodyBytes from apiserver/pkg/server/options/server_run_options.go to make this configurable
|
||||||
maxRequestSizeBytes = int64(3 * 1024 * 1024)
|
const maxRequestSizeBytes = apiservercel.DefaultMaxRequestSizeBytes
|
||||||
// OpenAPI duration strings follow RFC 3339, section 5.6 - see the comment on maxDatetimeSizeJSON
|
|
||||||
maxDurationSizeJSON = 32
|
|
||||||
// OpenAPI datetime strings follow RFC 3339, section 5.6, and the longest possible
|
|
||||||
// such string is 9999-12-31T23:59:59.999999999Z, which has length 30 - we add 2
|
|
||||||
// to allow for quotation marks
|
|
||||||
maxDatetimeSizeJSON = 32
|
|
||||||
// Golang allows a string of 0 to be parsed as a duration, so that plus 2 to account for
|
|
||||||
// quotation marks makes 3
|
|
||||||
minDurationSizeJSON = 3
|
|
||||||
// RFC 3339 dates require YYYY-MM-DD, and then we add 2 to allow for quotation marks
|
|
||||||
dateSizeJSON = 12
|
|
||||||
// RFC 3339 datetimes require a full date (YYYY-MM-DD) and full time (HH:MM:SS), and we add 3 for
|
|
||||||
// quotation marks like always in addition to the capital T that separates the date and time
|
|
||||||
minDatetimeSizeJSON = 21
|
|
||||||
// ""
|
|
||||||
minStringSize = 2
|
|
||||||
// true
|
|
||||||
minBoolSize = 4
|
|
||||||
// 0
|
|
||||||
minNumberSize = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// SchemaDeclType converts the structural schema to a CEL declaration, or returns nil if the
|
// SchemaDeclType converts the structural schema to a CEL declaration, or returns nil if the
|
||||||
// the structural schema should not be exposed in CEL expressions.
|
// structural schema should not be exposed in CEL expressions.
|
||||||
// Set isResourceRoot to true for the root of a custom resource or embedded resource.
|
// Set isResourceRoot to true for the root of a custom resource or embedded resource.
|
||||||
//
|
//
|
||||||
// Schemas with XPreserveUnknownFields not exposed unless they are objects. Array and "maps" schemas
|
// Schemas with XPreserveUnknownFields not exposed unless they are objects. Array and "maps" schemas
|
||||||
@ -60,7 +39,7 @@ const (
|
|||||||
// if their schema is not exposed.
|
// if their schema is not exposed.
|
||||||
//
|
//
|
||||||
// The CEL declaration for objects with XPreserveUnknownFields does not expose unknown fields.
|
// The CEL declaration for objects with XPreserveUnknownFields does not expose unknown fields.
|
||||||
func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *apiservercel.DeclType {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -77,7 +56,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
// To validate requirements on both the int and string representation:
|
// To validate requirements on both the int and string representation:
|
||||||
// `type(intOrStringField) == int ? intOrStringField < 5 : double(intOrStringField.replace('%', '')) < 0.5
|
// `type(intOrStringField) == int ? intOrStringField < 5 : double(intOrStringField.replace('%', '')) < 0.5
|
||||||
//
|
//
|
||||||
dyn := newSimpleTypeWithMinSize("dyn", cel.DynType, nil, 1) // smallest value for a serialied x-kubernetes-int-or-string is 0
|
dyn := apiservercel.NewSimpleTypeWithMinSize("dyn", cel.DynType, nil, 1) // smallest value for a serialized x-kubernetes-int-or-string is 0
|
||||||
// handle x-kubernetes-int-or-string by returning the max length/min serialized size of the largest possible string
|
// handle x-kubernetes-int-or-string by returning the max length/min serialized size of the largest possible string
|
||||||
dyn.MaxElements = maxRequestSizeBytes - 2
|
dyn.MaxElements = maxRequestSizeBytes - 2
|
||||||
return dyn
|
return dyn
|
||||||
@ -106,7 +85,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
} else {
|
} else {
|
||||||
maxItems = estimateMaxArrayItemsFromMinSize(itemsType.MinSerializedSize)
|
maxItems = estimateMaxArrayItemsFromMinSize(itemsType.MinSerializedSize)
|
||||||
}
|
}
|
||||||
return NewListType(itemsType, maxItems)
|
return apiservercel.NewListType(itemsType, maxItems)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case "object":
|
case "object":
|
||||||
@ -119,11 +98,11 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
} else {
|
} else {
|
||||||
maxProperties = estimateMaxAdditionalPropertiesFromMinSize(propsType.MinSerializedSize)
|
maxProperties = estimateMaxAdditionalPropertiesFromMinSize(propsType.MinSerializedSize)
|
||||||
}
|
}
|
||||||
return NewMapType(StringType, propsType, maxProperties)
|
return apiservercel.NewMapType(apiservercel.StringType, propsType, maxProperties)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fields := make(map[string]*DeclField, len(s.Properties))
|
fields := make(map[string]*apiservercel.DeclField, len(s.Properties))
|
||||||
|
|
||||||
required := map[string]bool{}
|
required := map[string]bool{}
|
||||||
if s.ValueValidation != nil {
|
if s.ValueValidation != nil {
|
||||||
@ -141,14 +120,8 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fieldType := SchemaDeclType(&prop, prop.XEmbeddedResource); fieldType != nil {
|
if fieldType := SchemaDeclType(&prop, prop.XEmbeddedResource); fieldType != nil {
|
||||||
if propName, ok := Escape(name); ok {
|
if propName, ok := apiservercel.Escape(name); ok {
|
||||||
fields[propName] = &DeclField{
|
fields[propName] = apiservercel.NewDeclField(propName, fieldType, required[name], enumValues, prop.Default.Object)
|
||||||
Name: propName,
|
|
||||||
Required: required[name],
|
|
||||||
Type: fieldType,
|
|
||||||
defaultValue: prop.Default.Object,
|
|
||||||
enumValues: enumValues, // Enum values are represented as strings in CEL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// the min serialized size for an object is 2 (for {}) plus the min size of all its required
|
// the min serialized size for an object is 2 (for {}) plus the min size of all its required
|
||||||
// properties
|
// properties
|
||||||
@ -159,14 +132,14 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objType := NewObjectType("object", fields)
|
objType := apiservercel.NewObjectType("object", fields)
|
||||||
objType.MinSerializedSize = minSerializedSize
|
objType.MinSerializedSize = minSerializedSize
|
||||||
return objType
|
return objType
|
||||||
case "string":
|
case "string":
|
||||||
if s.ValueValidation != nil {
|
if s.ValueValidation != nil {
|
||||||
switch s.ValueValidation.Format {
|
switch s.ValueValidation.Format {
|
||||||
case "byte":
|
case "byte":
|
||||||
byteWithMaxLength := newSimpleTypeWithMinSize("bytes", cel.BytesType, types.Bytes([]byte{}), minStringSize)
|
byteWithMaxLength := apiservercel.NewSimpleTypeWithMinSize("bytes", cel.BytesType, types.Bytes([]byte{}), apiservercel.MinStringSize)
|
||||||
if s.ValueValidation.MaxLength != nil {
|
if s.ValueValidation.MaxLength != nil {
|
||||||
byteWithMaxLength.MaxElements = zeroIfNegative(*s.ValueValidation.MaxLength)
|
byteWithMaxLength.MaxElements = zeroIfNegative(*s.ValueValidation.MaxLength)
|
||||||
} else {
|
} else {
|
||||||
@ -174,20 +147,20 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
}
|
}
|
||||||
return byteWithMaxLength
|
return byteWithMaxLength
|
||||||
case "duration":
|
case "duration":
|
||||||
durationWithMaxLength := newSimpleTypeWithMinSize("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}, int64(minDurationSizeJSON))
|
durationWithMaxLength := apiservercel.NewSimpleTypeWithMinSize("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}, int64(apiservercel.MinDurationSizeJSON))
|
||||||
durationWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
durationWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||||
return durationWithMaxLength
|
return durationWithMaxLength
|
||||||
case "date":
|
case "date":
|
||||||
timestampWithMaxLength := newSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, int64(dateSizeJSON))
|
timestampWithMaxLength := apiservercel.NewSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, int64(apiservercel.JSONDateSize))
|
||||||
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||||
return timestampWithMaxLength
|
return timestampWithMaxLength
|
||||||
case "date-time":
|
case "date-time":
|
||||||
timestampWithMaxLength := newSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, int64(minDatetimeSizeJSON))
|
timestampWithMaxLength := apiservercel.NewSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, int64(apiservercel.MinDatetimeSizeJSON))
|
||||||
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||||
return timestampWithMaxLength
|
return timestampWithMaxLength
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strWithMaxLength := newSimpleTypeWithMinSize("string", cel.StringType, types.String(""), minStringSize)
|
strWithMaxLength := apiservercel.NewSimpleTypeWithMinSize("string", cel.StringType, types.String(""), apiservercel.MinStringSize)
|
||||||
if s.ValueValidation != nil && s.ValueValidation.MaxLength != nil {
|
if s.ValueValidation != nil && s.ValueValidation.MaxLength != nil {
|
||||||
// multiply the user-provided max length by 4 in the case of an otherwise-untyped string
|
// 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,
|
// we do this because the OpenAPIv3 spec indicates that maxLength is specified in runes/code points,
|
||||||
@ -199,11 +172,11 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
}
|
}
|
||||||
return strWithMaxLength
|
return strWithMaxLength
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return BoolType
|
return apiservercel.BoolType
|
||||||
case "number":
|
case "number":
|
||||||
return DoubleType
|
return apiservercel.DoubleType
|
||||||
case "integer":
|
case "integer":
|
||||||
return IntType
|
return apiservercel.IntType
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -268,18 +241,18 @@ func MaxCardinality(minSize int64) uint64 {
|
|||||||
func estimateMaxStringLengthPerRequest(s *schema.Structural) int64 {
|
func estimateMaxStringLengthPerRequest(s *schema.Structural) int64 {
|
||||||
if s.ValueValidation == nil || s.XIntOrString {
|
if s.ValueValidation == nil || s.XIntOrString {
|
||||||
// subtract 2 to account for ""
|
// subtract 2 to account for ""
|
||||||
return (maxRequestSizeBytes - 2)
|
return maxRequestSizeBytes - 2
|
||||||
}
|
}
|
||||||
switch s.ValueValidation.Format {
|
switch s.ValueValidation.Format {
|
||||||
case "duration":
|
case "duration":
|
||||||
return maxDurationSizeJSON
|
return apiservercel.MaxDurationSizeJSON
|
||||||
case "date":
|
case "date":
|
||||||
return dateSizeJSON
|
return apiservercel.JSONDateSize
|
||||||
case "date-time":
|
case "date-time":
|
||||||
return maxDatetimeSizeJSON
|
return apiservercel.MaxDatetimeSizeJSON
|
||||||
default:
|
default:
|
||||||
// subtract 2 to account for ""
|
// subtract 2 to account for ""
|
||||||
return (maxRequestSizeBytes - 2)
|
return maxRequestSizeBytes - 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSchemaDeclType(t *testing.T) {
|
func TestSchemaDeclType(t *testing.T) {
|
||||||
@ -86,15 +87,15 @@ func TestSchemaDeclType(t *testing.T) {
|
|||||||
func TestSchemaDeclTypes(t *testing.T) {
|
func TestSchemaDeclTypes(t *testing.T) {
|
||||||
ts := testSchema()
|
ts := testSchema()
|
||||||
cust := SchemaDeclType(ts, true).MaybeAssignTypeName("CustomObject")
|
cust := SchemaDeclType(ts, true).MaybeAssignTypeName("CustomObject")
|
||||||
typeMap := FieldTypeMap("CustomObject", cust)
|
typeMap := apiservercel.FieldTypeMap("CustomObject", cust)
|
||||||
nested, _ := cust.FindField("nested")
|
nested, _ := cust.FindField("nested")
|
||||||
metadata, _ := cust.FindField("metadata")
|
metadata, _ := cust.FindField("metadata")
|
||||||
expectedObjTypeMap := map[string]*DeclType{
|
expectedObjTypeMap := map[string]*apiservercel.DeclType{
|
||||||
"CustomObject": cust,
|
"CustomObject": cust,
|
||||||
"CustomObject.nested": nested.Type,
|
"CustomObject.nested": nested.Type,
|
||||||
"CustomObject.metadata": metadata.Type,
|
"CustomObject.metadata": metadata.Type,
|
||||||
}
|
}
|
||||||
objTypeMap := map[string]*DeclType{}
|
objTypeMap := map[string]*apiservercel.DeclType{}
|
||||||
for name, t := range typeMap {
|
for name, t := range typeMap {
|
||||||
if t.IsObject() {
|
if t.IsObject() {
|
||||||
objTypeMap[name] = t
|
objTypeMap[name] = t
|
||||||
@ -406,7 +407,7 @@ func TestEstimateMaxLengthJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// should be exactly equal to maxDurationSizeJSON
|
// should be exactly equal to maxDurationSizeJSON
|
||||||
ExpectedMaxElements: maxDurationSizeJSON,
|
ExpectedMaxElements: apiservercel.MaxDurationSizeJSON,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "dateSize",
|
Name: "dateSize",
|
||||||
@ -419,7 +420,7 @@ func TestEstimateMaxLengthJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// should be exactly equal to dateSizeJSON
|
// should be exactly equal to dateSizeJSON
|
||||||
ExpectedMaxElements: dateSizeJSON,
|
ExpectedMaxElements: apiservercel.JSONDateSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "maxdatetimeSize",
|
Name: "maxdatetimeSize",
|
||||||
@ -432,7 +433,7 @@ func TestEstimateMaxLengthJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// should be exactly equal to maxDatetimeSizeJSON
|
// should be exactly equal to maxDatetimeSizeJSON
|
||||||
ExpectedMaxElements: maxDatetimeSizeJSON,
|
ExpectedMaxElements: apiservercel.MaxDatetimeSizeJSON,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "maxintOrStringSize",
|
Name: "maxintOrStringSize",
|
||||||
@ -442,7 +443,7 @@ func TestEstimateMaxLengthJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// should be exactly equal to maxRequestSizeBytes - 2 (to allow for quotes in the case of a string)
|
// should be exactly equal to maxRequestSizeBytes - 2 (to allow for quotes in the case of a string)
|
||||||
ExpectedMaxElements: maxRequestSizeBytes - 2,
|
ExpectedMaxElements: apiservercel.DefaultMaxRequestSizeBytes - 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "objectDefaultFieldArray",
|
Name: "objectDefaultFieldArray",
|
||||||
@ -495,7 +496,7 @@ func TestEstimateMaxLengthJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// note that unlike regular strings we don't have to take unicode into account,
|
// note that unlike regular strings we don't have to take unicode into account,
|
||||||
// so we we expect the max length to be exactly equal to the user-supplied one
|
// so we expect the max length to be exactly equal to the user-supplied one
|
||||||
ExpectedMaxElements: 20,
|
ExpectedMaxElements: 20,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23,61 +23,14 @@ import (
|
|||||||
"github.com/google/cel-go/common/types"
|
"github.com/google/cel-go/common/types"
|
||||||
|
|
||||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||||
|
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTypes_ListType(t *testing.T) {
|
|
||||||
list := NewListType(StringType, -1)
|
|
||||||
if !list.IsList() {
|
|
||||||
t.Error("list type not identifiable as list")
|
|
||||||
}
|
|
||||||
if list.TypeName() != "list" {
|
|
||||||
t.Errorf("got %s, wanted list", list.TypeName())
|
|
||||||
}
|
|
||||||
if list.DefaultValue() == nil {
|
|
||||||
t.Error("got nil zero value for list type")
|
|
||||||
}
|
|
||||||
if list.ElemType.TypeName() != "string" {
|
|
||||||
t.Errorf("got %s, wanted elem type of string", list.ElemType.TypeName())
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTypes_MapType(t *testing.T) {
|
|
||||||
mp := NewMapType(StringType, IntType, -1)
|
|
||||||
if !mp.IsMap() {
|
|
||||||
t.Error("map type not identifiable as map")
|
|
||||||
}
|
|
||||||
if mp.TypeName() != "map" {
|
|
||||||
t.Errorf("got %s, wanted map", mp.TypeName())
|
|
||||||
}
|
|
||||||
if mp.DefaultValue() == nil {
|
|
||||||
t.Error("got nil zero value for map type")
|
|
||||||
}
|
|
||||||
if mp.KeyType.TypeName() != "string" {
|
|
||||||
t.Errorf("got %s, wanted key type of string", mp.KeyType.TypeName())
|
|
||||||
}
|
|
||||||
if mp.ElemType.TypeName() != "int" {
|
|
||||||
t.Errorf("got %s, wanted elem type of int", mp.ElemType.TypeName())
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTypes_RuleTypesFieldMapping(t *testing.T) {
|
func TestTypes_RuleTypesFieldMapping(t *testing.T) {
|
||||||
stdEnv, _ := cel.NewEnv()
|
stdEnv, _ := cel.NewEnv()
|
||||||
reg := NewRegistry(stdEnv)
|
reg := apiservercel.NewRegistry(stdEnv)
|
||||||
rt, err := NewRuleTypes("CustomObject", SchemaDeclType(testSchema(), true), reg)
|
rt, err := apiservercel.NewRuleTypes("CustomObject", SchemaDeclType(testSchema(), true), reg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -112,22 +65,22 @@ func TestTypes_RuleTypesFieldMapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Manually constructed instance of the schema.
|
// Manually constructed instance of the schema.
|
||||||
name := NewField(1, "name")
|
name := apiservercel.NewField(1, "name")
|
||||||
name.Ref = testValue(t, 2, "test-instance")
|
name.Ref = testValue(t, 2, "test-instance")
|
||||||
nestedVal := NewMapValue()
|
nestedVal := apiservercel.NewMapValue()
|
||||||
flags := NewField(5, "flags")
|
flags := apiservercel.NewField(5, "flags")
|
||||||
flagsVal := NewMapValue()
|
flagsVal := apiservercel.NewMapValue()
|
||||||
myFlag := NewField(6, "my_flag")
|
myFlag := apiservercel.NewField(6, "my_flag")
|
||||||
myFlag.Ref = testValue(t, 7, true)
|
myFlag.Ref = testValue(t, 7, true)
|
||||||
flagsVal.AddField(myFlag)
|
flagsVal.AddField(myFlag)
|
||||||
flags.Ref = testValue(t, 8, flagsVal)
|
flags.Ref = testValue(t, 8, flagsVal)
|
||||||
dates := NewField(9, "dates")
|
dates := apiservercel.NewField(9, "dates")
|
||||||
dates.Ref = testValue(t, 10, NewListValue())
|
dates.Ref = testValue(t, 10, apiservercel.NewListValue())
|
||||||
nestedVal.AddField(flags)
|
nestedVal.AddField(flags)
|
||||||
nestedVal.AddField(dates)
|
nestedVal.AddField(dates)
|
||||||
nested := NewField(3, "nested")
|
nested := apiservercel.NewField(3, "nested")
|
||||||
nested.Ref = testValue(t, 4, nestedVal)
|
nested.Ref = testValue(t, 4, nestedVal)
|
||||||
mapVal := NewMapValue()
|
mapVal := apiservercel.NewMapValue()
|
||||||
mapVal.AddField(name)
|
mapVal.AddField(name)
|
||||||
mapVal.AddField(nested)
|
mapVal.AddField(nested)
|
||||||
//rule := rt.ConvertToRule(testValue(t, 11, mapVal))
|
//rule := rt.ConvertToRule(testValue(t, 11, mapVal))
|
||||||
@ -156,11 +109,11 @@ func TestTypes_RuleTypesFieldMapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testValue(t *testing.T, id int64, val interface{}) *DynValue {
|
func testValue(t *testing.T, id int64, val interface{}) *apiservercel.DynValue {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dv, err := NewDynValue(id, val)
|
dv, err := apiservercel.NewDynValue(id, val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("model.NewDynValue(%d, %v) failed: %v", id, val, err)
|
t.Fatalf("NewDynValue(%d, %v) failed: %v", id, val, err)
|
||||||
}
|
}
|
||||||
return dv
|
return dv
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,10 @@ import (
|
|||||||
|
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/metrics"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
"k8s.io/apiserver/pkg/cel"
|
||||||
|
"k8s.io/apiserver/pkg/cel/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validator parallels the structure of schema.Structural and includes the compiled CEL programs
|
// Validator parallels the structure of schema.Structural and includes the compiled CEL programs
|
||||||
@ -74,7 +75,7 @@ func NewValidator(s *schema.Structural, isResourceRoot bool, perCallLimit uint64
|
|||||||
// validator creates a Validator for all x-kubernetes-validations at the level of the provided schema and lower and
|
// validator creates a Validator for all x-kubernetes-validations at the level of the provided schema and lower and
|
||||||
// returns the Validator if any x-kubernetes-validations exist in the schema, or nil if no x-kubernetes-validations
|
// returns the Validator if any x-kubernetes-validations exist in the schema, or nil if no x-kubernetes-validations
|
||||||
// exist. declType is expected to be a CEL DeclType corresponding to the structural schema.
|
// exist. declType is expected to be a CEL DeclType corresponding to the structural schema.
|
||||||
func validator(s *schema.Structural, isResourceRoot bool, declType *model.DeclType, perCallLimit uint64) *Validator {
|
func validator(s *schema.Structural, isResourceRoot bool, declType *cel.DeclType, perCallLimit uint64) *Validator {
|
||||||
compiledRules, err := Compile(s, declType, perCallLimit)
|
compiledRules, err := Compile(s, declType, perCallLimit)
|
||||||
var itemsValidator, additionalPropertiesValidator *Validator
|
var itemsValidator, additionalPropertiesValidator *Validator
|
||||||
var propertiesValidators map[string]Validator
|
var propertiesValidators map[string]Validator
|
||||||
@ -85,8 +86,8 @@ func validator(s *schema.Structural, isResourceRoot bool, declType *model.DeclTy
|
|||||||
propertiesValidators = make(map[string]Validator, len(s.Properties))
|
propertiesValidators = make(map[string]Validator, len(s.Properties))
|
||||||
for k, p := range s.Properties {
|
for k, p := range s.Properties {
|
||||||
prop := p
|
prop := p
|
||||||
var fieldType *model.DeclType
|
var fieldType *cel.DeclType
|
||||||
if escapedPropName, ok := model.Escape(k); ok {
|
if escapedPropName, ok := cel.Escape(k); ok {
|
||||||
if f, ok := declType.Fields[escapedPropName]; ok {
|
if f, ok := declType.Fields[escapedPropName]; ok {
|
||||||
fieldType = f.Type
|
fieldType = f.Type
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,10 +25,11 @@ import (
|
|||||||
"github.com/google/cel-go/common/types"
|
"github.com/google/cel-go/common/types"
|
||||||
"github.com/google/cel-go/common/types/ref"
|
"github.com/google/cel-go/common/types/ref"
|
||||||
"github.com/google/cel-go/common/types/traits"
|
"github.com/google/cel-go/common/types/traits"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
|
||||||
|
|
||||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||||
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apiserver/pkg/cel"
|
||||||
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
"k8s.io/kube-openapi/pkg/validation/strfmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -343,7 +344,7 @@ func (t *unstructuredMapList) Add(other ref.Val) ref.Val {
|
|||||||
func escapeKeyProps(idents []string) []string {
|
func escapeKeyProps(idents []string) []string {
|
||||||
result := make([]string, len(idents))
|
result := make([]string, len(idents))
|
||||||
for i, prop := range idents {
|
for i, prop := range idents {
|
||||||
if escaped, ok := model.Escape(prop); ok {
|
if escaped, ok := cel.Escape(prop); ok {
|
||||||
result[i] = escaped
|
result[i] = escaped
|
||||||
} else {
|
} else {
|
||||||
result[i] = prop
|
result[i] = prop
|
||||||
@ -644,7 +645,7 @@ func (t *unstructuredMap) Iterator() traits.Iterator {
|
|||||||
if _, ok := t.propSchema(k); ok {
|
if _, ok := t.propSchema(k); ok {
|
||||||
mapKey := k
|
mapKey := k
|
||||||
if isObject {
|
if isObject {
|
||||||
if escaped, ok := model.Escape(k); ok {
|
if escaped, ok := cel.Escape(k); ok {
|
||||||
mapKey = escaped
|
mapKey = escaped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -683,7 +684,7 @@ func (t *unstructuredMap) Find(key ref.Val) (ref.Val, bool) {
|
|||||||
}
|
}
|
||||||
k := keyStr.Value().(string)
|
k := keyStr.Value().(string)
|
||||||
if isObject {
|
if isObject {
|
||||||
k, ok = model.Unescape(k)
|
k, ok = cel.Unescape(k)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ require (
|
|||||||
github.com/evanphx/json-patch v4.12.0+incompatible
|
github.com/evanphx/json-patch v4.12.0+incompatible
|
||||||
github.com/fsnotify/fsnotify v1.5.4
|
github.com/fsnotify/fsnotify v1.5.4
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
|
github.com/google/cel-go v0.12.5
|
||||||
github.com/google/gnostic v0.5.7-v3refs
|
github.com/google/gnostic v0.5.7-v3refs
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/gofuzz v1.1.0
|
github.com/google/gofuzz v1.1.0
|
||||||
@ -36,7 +37,9 @@ require (
|
|||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
|
||||||
|
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21
|
||||||
google.golang.org/grpc v1.49.0
|
google.golang.org/grpc v1.49.0
|
||||||
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gopkg.in/square/go-jose.v2 v2.2.2
|
gopkg.in/square/go-jose.v2 v2.2.2
|
||||||
k8s.io/api v0.0.0
|
k8s.io/api v0.0.0
|
||||||
@ -56,6 +59,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.97.0 // indirect
|
cloud.google.com/go v0.97.0 // indirect
|
||||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||||
@ -95,6 +99,7 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||||
github.com/spf13/cobra v1.5.0 // indirect
|
github.com/spf13/cobra v1.5.0 // indirect
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
go.etcd.io/bbolt v1.3.6 // indirect
|
||||||
@ -111,8 +116,6 @@ require (
|
|||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
golang.org/x/text v0.3.8 // indirect
|
golang.org/x/text v0.3.8 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
|
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
5
staging/src/k8s.io/apiserver/go.sum
generated
5
staging/src/k8s.io/apiserver/go.sum
generated
@ -57,6 +57,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves=
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
@ -220,6 +222,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
|||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||||
|
github.com/google/cel-go v0.12.5 h1:DmzaiSgoaqGCjtpPQWl26/gND+yRpim56H1jCVev6d8=
|
||||||
|
github.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=
|
||||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
||||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
@ -439,6 +443,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -22,7 +22,8 @@ import (
|
|||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"github.com/google/cel-go/common/types"
|
"github.com/google/cel-go/common/types"
|
||||||
"github.com/google/cel-go/common/types/ref"
|
"github.com/google/cel-go/common/types/ref"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
|
||||||
|
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// URLs provides a CEL function library extension of URL parsing functions.
|
// URLs provides a CEL function library extension of URL parsing functions.
|
||||||
@ -113,25 +114,25 @@ type urls struct{}
|
|||||||
|
|
||||||
var urlLibraryDecls = map[string][]cel.FunctionOpt{
|
var urlLibraryDecls = map[string][]cel.FunctionOpt{
|
||||||
"url": {
|
"url": {
|
||||||
cel.Overload("string_to_url", []*cel.Type{cel.StringType}, model.URLType,
|
cel.Overload("string_to_url", []*cel.Type{cel.StringType}, apiservercel.URLType,
|
||||||
cel.UnaryBinding(stringToUrl))},
|
cel.UnaryBinding(stringToUrl))},
|
||||||
"getScheme": {
|
"getScheme": {
|
||||||
cel.MemberOverload("url_get_scheme", []*cel.Type{model.URLType}, cel.StringType,
|
cel.MemberOverload("url_get_scheme", []*cel.Type{apiservercel.URLType}, cel.StringType,
|
||||||
cel.UnaryBinding(getScheme))},
|
cel.UnaryBinding(getScheme))},
|
||||||
"getHost": {
|
"getHost": {
|
||||||
cel.MemberOverload("url_get_host", []*cel.Type{model.URLType}, cel.StringType,
|
cel.MemberOverload("url_get_host", []*cel.Type{apiservercel.URLType}, cel.StringType,
|
||||||
cel.UnaryBinding(getHost))},
|
cel.UnaryBinding(getHost))},
|
||||||
"getHostname": {
|
"getHostname": {
|
||||||
cel.MemberOverload("url_get_hostname", []*cel.Type{model.URLType}, cel.StringType,
|
cel.MemberOverload("url_get_hostname", []*cel.Type{apiservercel.URLType}, cel.StringType,
|
||||||
cel.UnaryBinding(getHostname))},
|
cel.UnaryBinding(getHostname))},
|
||||||
"getPort": {
|
"getPort": {
|
||||||
cel.MemberOverload("url_get_port", []*cel.Type{model.URLType}, cel.StringType,
|
cel.MemberOverload("url_get_port", []*cel.Type{apiservercel.URLType}, cel.StringType,
|
||||||
cel.UnaryBinding(getPort))},
|
cel.UnaryBinding(getPort))},
|
||||||
"getEscapedPath": {
|
"getEscapedPath": {
|
||||||
cel.MemberOverload("url_get_escaped_path", []*cel.Type{model.URLType}, cel.StringType,
|
cel.MemberOverload("url_get_escaped_path", []*cel.Type{apiservercel.URLType}, cel.StringType,
|
||||||
cel.UnaryBinding(getEscapedPath))},
|
cel.UnaryBinding(getEscapedPath))},
|
||||||
"getQuery": {
|
"getQuery": {
|
||||||
cel.MemberOverload("url_get_query", []*cel.Type{model.URLType},
|
cel.MemberOverload("url_get_query", []*cel.Type{apiservercel.URLType},
|
||||||
cel.MapType(cel.StringType, cel.ListType(cel.StringType)),
|
cel.MapType(cel.StringType, cel.ListType(cel.StringType)),
|
||||||
cel.UnaryBinding(getQuery))},
|
cel.UnaryBinding(getQuery))},
|
||||||
"isURL": {
|
"isURL": {
|
||||||
@ -169,7 +170,7 @@ func stringToUrl(arg ref.Val) ref.Val {
|
|||||||
// Errors are not expected here since Parse is a more lenient parser than ParseRequestURI.
|
// Errors are not expected here since Parse is a more lenient parser than ParseRequestURI.
|
||||||
return types.NewErr("URL parse error during conversion from string: %v", err)
|
return types.NewErr("URL parse error during conversion from string: %v", err)
|
||||||
}
|
}
|
||||||
return model.URL{URL: u}
|
return apiservercel.URL{URL: u}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getScheme(arg ref.Val) ref.Val {
|
func getScheme(arg ref.Val) ref.Val {
|
48
staging/src/k8s.io/apiserver/pkg/cel/limits.go
Normal file
48
staging/src/k8s.io/apiserver/pkg/cel/limits.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cel
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultMaxRequestSizeBytes is the size of the largest request that will be accepted
|
||||||
|
DefaultMaxRequestSizeBytes = int64(3 * 1024 * 1024)
|
||||||
|
|
||||||
|
// MaxDurationSizeJSON
|
||||||
|
// OpenAPI duration strings follow RFC 3339, section 5.6 - see the comment on maxDatetimeSizeJSON
|
||||||
|
MaxDurationSizeJSON = 32
|
||||||
|
// MaxDatetimeSizeJSON
|
||||||
|
// OpenAPI datetime strings follow RFC 3339, section 5.6, and the longest possible
|
||||||
|
// such string is 9999-12-31T23:59:59.999999999Z, which has length 30 - we add 2
|
||||||
|
// to allow for quotation marks
|
||||||
|
MaxDatetimeSizeJSON = 32
|
||||||
|
// MinDurationSizeJSON
|
||||||
|
// Golang allows a string of 0 to be parsed as a duration, so that plus 2 to account for
|
||||||
|
// quotation marks makes 3
|
||||||
|
MinDurationSizeJSON = 3
|
||||||
|
// JSONDateSize is the size of a date serialized as part of a JSON object
|
||||||
|
// RFC 3339 dates require YYYY-MM-DD, and then we add 2 to allow for quotation marks
|
||||||
|
JSONDateSize = 12
|
||||||
|
// MinDatetimeSizeJSON is the minimal length of a datetime formatted as RFC 3339
|
||||||
|
// RFC 3339 datetimes require a full date (YYYY-MM-DD) and full time (HH:MM:SS), and we add 3 for
|
||||||
|
// quotation marks like always in addition to the capital T that separates the date and time
|
||||||
|
MinDatetimeSizeJSON = 21
|
||||||
|
// MinStringSize is the size of literal ""
|
||||||
|
MinStringSize = 2
|
||||||
|
// MinBoolSize is the length of literal true
|
||||||
|
MinBoolSize = 4
|
||||||
|
// MinNumberSize is the length of literal 0
|
||||||
|
MinNumberSize = 1
|
||||||
|
)
|
@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Resolver declares methods to find policy templates and related configuration objects.
|
// Resolver declares methods to find policy templates and related configuration objects.
|
||||||
@ -30,12 +29,11 @@ type Resolver interface {
|
|||||||
FindType(name string) (*DeclType, bool)
|
FindType(name string) (*DeclType, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistry create a registry for keeping track of environments, schemas, templates, and more
|
// NewRegistry create a registry for keeping track of environments and types
|
||||||
// from a base cel.Env expression environment.
|
// from a base cel.Env expression environment.
|
||||||
func NewRegistry(stdExprEnv *cel.Env) *Registry {
|
func NewRegistry(stdExprEnv *cel.Env) *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
exprEnvs: map[string]*cel.Env{"": stdExprEnv},
|
exprEnvs: map[string]*cel.Env{"": stdExprEnv},
|
||||||
schemas: map[string]*schema.Structural{},
|
|
||||||
types: map[string]*DeclType{
|
types: map[string]*DeclType{
|
||||||
BoolType.TypeName(): BoolType,
|
BoolType.TypeName(): BoolType,
|
||||||
BytesType.TypeName(): BytesType,
|
BytesType.TypeName(): BytesType,
|
||||||
@ -58,7 +56,6 @@ func NewRegistry(stdExprEnv *cel.Env) *Registry {
|
|||||||
type Registry struct {
|
type Registry struct {
|
||||||
rwMux sync.RWMutex
|
rwMux sync.RWMutex
|
||||||
exprEnvs map[string]*cel.Env
|
exprEnvs map[string]*cel.Env
|
||||||
schemas map[string]*schema.Structural
|
|
||||||
types map[string]*DeclType
|
types map[string]*DeclType
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -79,7 +79,7 @@ func NewObjectType(name string, fields map[string]*DeclField) *DeclType {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSimpleTypeWithMinSize(name string, celType *cel.Type, zeroVal ref.Val, minSize int64) *DeclType {
|
func NewSimpleTypeWithMinSize(name string, celType *cel.Type, zeroVal ref.Val, minSize int64) *DeclType {
|
||||||
return &DeclType{
|
return &DeclType{
|
||||||
name: name,
|
name: name,
|
||||||
celType: celType,
|
celType: celType,
|
||||||
@ -284,6 +284,16 @@ type DeclField struct {
|
|||||||
defaultValue interface{}
|
defaultValue interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDeclField(name string, declType *DeclType, required bool, enumValues []interface{}, defaultValue interface{}) *DeclField {
|
||||||
|
return &DeclField{
|
||||||
|
Name: name,
|
||||||
|
Type: declType,
|
||||||
|
Required: required,
|
||||||
|
enumValues: enumValues,
|
||||||
|
defaultValue: defaultValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TypeName returns the string type name of the field.
|
// TypeName returns the string type name of the field.
|
||||||
func (f *DeclField) TypeName() string {
|
func (f *DeclField) TypeName() string {
|
||||||
return f.Type.TypeName()
|
return f.Type.TypeName()
|
||||||
@ -495,44 +505,44 @@ type schemaTypeProvider struct {
|
|||||||
var (
|
var (
|
||||||
// AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the
|
// AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the
|
||||||
// types supported.
|
// types supported.
|
||||||
AnyType = newSimpleTypeWithMinSize("any", cel.AnyType, nil, 1)
|
AnyType = NewSimpleTypeWithMinSize("any", cel.AnyType, nil, 1)
|
||||||
|
|
||||||
// BoolType is equivalent to the CEL 'bool' type.
|
// BoolType is equivalent to the CEL 'bool' type.
|
||||||
BoolType = newSimpleTypeWithMinSize("bool", cel.BoolType, types.False, minBoolSize)
|
BoolType = NewSimpleTypeWithMinSize("bool", cel.BoolType, types.False, MinBoolSize)
|
||||||
|
|
||||||
// BytesType is equivalent to the CEL 'bytes' type.
|
// BytesType is equivalent to the CEL 'bytes' type.
|
||||||
BytesType = newSimpleTypeWithMinSize("bytes", cel.BytesType, types.Bytes([]byte{}), minStringSize)
|
BytesType = NewSimpleTypeWithMinSize("bytes", cel.BytesType, types.Bytes([]byte{}), MinStringSize)
|
||||||
|
|
||||||
// DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value.
|
// DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value.
|
||||||
DoubleType = newSimpleTypeWithMinSize("double", cel.DoubleType, types.Double(0), minNumberSize)
|
DoubleType = NewSimpleTypeWithMinSize("double", cel.DoubleType, types.Double(0), MinNumberSize)
|
||||||
|
|
||||||
// DurationType is equivalent to the CEL 'duration' type.
|
// DurationType is equivalent to the CEL 'duration' type.
|
||||||
DurationType = newSimpleTypeWithMinSize("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}, minDurationSizeJSON)
|
DurationType = NewSimpleTypeWithMinSize("duration", cel.DurationType, types.Duration{Duration: time.Duration(0)}, MinDurationSizeJSON)
|
||||||
|
|
||||||
// DateType is equivalent to the CEL 'date' type.
|
// DateType is equivalent to the CEL 'date' type.
|
||||||
DateType = newSimpleTypeWithMinSize("date", cel.TimestampType, types.Timestamp{Time: time.Time{}}, dateSizeJSON)
|
DateType = NewSimpleTypeWithMinSize("date", cel.TimestampType, types.Timestamp{Time: time.Time{}}, JSONDateSize)
|
||||||
|
|
||||||
// DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be
|
// DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be
|
||||||
// determined at runtime rather than compile time.
|
// determined at runtime rather than compile time.
|
||||||
DynType = newSimpleTypeWithMinSize("dyn", cel.DynType, nil, 1)
|
DynType = NewSimpleTypeWithMinSize("dyn", cel.DynType, nil, 1)
|
||||||
|
|
||||||
// IntType is equivalent to the CEL 'int' type which is a 64-bit signed int.
|
// IntType is equivalent to the CEL 'int' type which is a 64-bit signed int.
|
||||||
IntType = newSimpleTypeWithMinSize("int", cel.IntType, types.IntZero, minNumberSize)
|
IntType = NewSimpleTypeWithMinSize("int", cel.IntType, types.IntZero, MinNumberSize)
|
||||||
|
|
||||||
// NullType is equivalent to the CEL 'null_type'.
|
// NullType is equivalent to the CEL 'null_type'.
|
||||||
NullType = newSimpleTypeWithMinSize("null_type", cel.NullType, types.NullValue, 4)
|
NullType = NewSimpleTypeWithMinSize("null_type", cel.NullType, types.NullValue, 4)
|
||||||
|
|
||||||
// StringType is equivalent to the CEL 'string' type which is expected to be a UTF-8 string.
|
// 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 values may either be string literals or expression strings.
|
||||||
StringType = newSimpleTypeWithMinSize("string", cel.StringType, types.String(""), minStringSize)
|
StringType = NewSimpleTypeWithMinSize("string", cel.StringType, types.String(""), MinStringSize)
|
||||||
|
|
||||||
// TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL.
|
// TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL.
|
||||||
// Note that both the OpenAPI date and date-time types map onto TimestampType, so not all types
|
// Note that both the OpenAPI date and date-time types map onto TimestampType, so not all types
|
||||||
// labeled as Timestamp will necessarily have the same MinSerializedSize.
|
// labeled as Timestamp will necessarily have the same MinSerializedSize.
|
||||||
TimestampType = newSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, dateSizeJSON)
|
TimestampType = NewSimpleTypeWithMinSize("timestamp", cel.TimestampType, types.Timestamp{Time: time.Time{}}, JSONDateSize)
|
||||||
|
|
||||||
// UintType is equivalent to the CEL 'uint' type.
|
// UintType is equivalent to the CEL 'uint' type.
|
||||||
UintType = newSimpleTypeWithMinSize("uint", cel.UintType, types.Uint(0), 1)
|
UintType = NewSimpleTypeWithMinSize("uint", cel.UintType, types.Uint(0), 1)
|
||||||
|
|
||||||
// ListType is equivalent to the CEL 'list' type.
|
// ListType is equivalent to the CEL 'list' type.
|
||||||
ListType = NewListType(AnyType, noMaxLength)
|
ListType = NewListType(AnyType, noMaxLength)
|
79
staging/src/k8s.io/apiserver/pkg/cel/types_test.go
Normal file
79
staging/src/k8s.io/apiserver/pkg/cel/types_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTypes_ListType(t *testing.T) {
|
||||||
|
list := NewListType(StringType, -1)
|
||||||
|
if !list.IsList() {
|
||||||
|
t.Error("list type not identifiable as list")
|
||||||
|
}
|
||||||
|
if list.TypeName() != "list" {
|
||||||
|
t.Errorf("got %s, wanted list", list.TypeName())
|
||||||
|
}
|
||||||
|
if list.DefaultValue() == nil {
|
||||||
|
t.Error("got nil zero value for list type")
|
||||||
|
}
|
||||||
|
if list.ElemType.TypeName() != "string" {
|
||||||
|
t.Errorf("got %s, wanted elem type of string", list.ElemType.TypeName())
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypes_MapType(t *testing.T) {
|
||||||
|
mp := NewMapType(StringType, IntType, -1)
|
||||||
|
if !mp.IsMap() {
|
||||||
|
t.Error("map type not identifiable as map")
|
||||||
|
}
|
||||||
|
if mp.TypeName() != "map" {
|
||||||
|
t.Errorf("got %s, wanted map", mp.TypeName())
|
||||||
|
}
|
||||||
|
if mp.DefaultValue() == nil {
|
||||||
|
t.Error("got nil zero value for map type")
|
||||||
|
}
|
||||||
|
if mp.KeyType.TypeName() != "string" {
|
||||||
|
t.Errorf("got %s, wanted key type of string", mp.KeyType.TypeName())
|
||||||
|
}
|
||||||
|
if mp.ElemType.TypeName() != "int" {
|
||||||
|
t.Errorf("got %s, wanted elem type of int", mp.ElemType.TypeName())
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testValue(t *testing.T, id int64, val interface{}) *DynValue {
|
||||||
|
t.Helper()
|
||||||
|
dv, err := NewDynValue(id, val)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewDynValue(%d, %v) failed: %v", id, val, err)
|
||||||
|
}
|
||||||
|
return dv
|
||||||
|
}
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package model
|
package cel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -366,7 +366,7 @@ func NewConfig(codecs serializer.CodecFactory) *Config {
|
|||||||
// A request body might be encoded in json, and is converted to
|
// A request body might be encoded in json, and is converted to
|
||||||
// proto when persisted in etcd, so we allow 2x as the largest request
|
// proto when persisted in etcd, so we allow 2x as the largest request
|
||||||
// body size to be accepted and decoded in a write request.
|
// body size to be accepted and decoded in a write request.
|
||||||
// If this constant is changed, maxRequestSizeBytes in apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go
|
// If this constant is changed, DefaultMaxRequestSizeBytes in k8s.io/apiserver/pkg/cel/limits.go
|
||||||
// should be changed to reflect the new value, if the two haven't
|
// should be changed to reflect the new value, if the two haven't
|
||||||
// been wired together already somehow.
|
// been wired together already somehow.
|
||||||
MaxRequestBodyBytes: int64(3 * 1024 * 1024),
|
MaxRequestBodyBytes: int64(3 * 1024 * 1024),
|
||||||
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@ -1321,8 +1321,6 @@ k8s.io/apiextensions-apiserver/pkg/apiserver
|
|||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/conversion
|
k8s.io/apiextensions-apiserver/pkg/apiserver/conversion
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema
|
k8s.io/apiextensions-apiserver/pkg/apiserver/schema
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel
|
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library
|
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/metrics
|
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model
|
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting
|
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting
|
||||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype
|
k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype
|
||||||
@ -1492,6 +1490,9 @@ k8s.io/apiserver/pkg/authorization/authorizer
|
|||||||
k8s.io/apiserver/pkg/authorization/authorizerfactory
|
k8s.io/apiserver/pkg/authorization/authorizerfactory
|
||||||
k8s.io/apiserver/pkg/authorization/path
|
k8s.io/apiserver/pkg/authorization/path
|
||||||
k8s.io/apiserver/pkg/authorization/union
|
k8s.io/apiserver/pkg/authorization/union
|
||||||
|
k8s.io/apiserver/pkg/cel
|
||||||
|
k8s.io/apiserver/pkg/cel/library
|
||||||
|
k8s.io/apiserver/pkg/cel/metrics
|
||||||
k8s.io/apiserver/pkg/endpoints
|
k8s.io/apiserver/pkg/endpoints
|
||||||
k8s.io/apiserver/pkg/endpoints/deprecation
|
k8s.io/apiserver/pkg/endpoints/deprecation
|
||||||
k8s.io/apiserver/pkg/endpoints/discovery
|
k8s.io/apiserver/pkg/endpoints/discovery
|
||||||
|
Loading…
Reference in New Issue
Block a user