update dependencies (#6267)
Signed-off-by: hongming <coder.scala@gmail.com>
This commit is contained in:
10
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go
generated
vendored
10
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -122,10 +123,13 @@ func Compile(s *schema.Structural, declType *apiservercel.DeclType, perCallLimit
|
||||
metrics.Metrics.ObserveCompilation(time.Since(t))
|
||||
}()
|
||||
|
||||
if len(s.Extensions.XValidations) == 0 {
|
||||
if len(s.XValidations) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
celRules := s.Extensions.XValidations
|
||||
if declType == nil {
|
||||
return nil, errors.New("failed to convert to declType for CEL validation rules")
|
||||
}
|
||||
celRules := s.XValidations
|
||||
|
||||
oldSelfEnvSet, optionalOldSelfEnvSet, err := prepareEnvSet(baseEnvSet, declType)
|
||||
if err != nil {
|
||||
@@ -282,7 +286,7 @@ func compileRule(s *schema.Structural, rule apiextensions.ValidationRule, envSet
|
||||
compilationResult.MessageExpressionMaxCost = costEst.Max
|
||||
}
|
||||
if rule.FieldPath != "" {
|
||||
validFieldPath, err := ValidFieldPath(rule.FieldPath, s)
|
||||
validFieldPath, _, err := ValidFieldPath(rule.FieldPath, s)
|
||||
if err == nil {
|
||||
compilationResult.NormalizedRuleFieldPath = validFieldPath.String()
|
||||
}
|
||||
|
||||
16
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go
generated
vendored
16
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go
generated
vendored
@@ -62,6 +62,9 @@ func (s *Structural) Pattern() string {
|
||||
}
|
||||
|
||||
func (s *Structural) Items() common.Schema {
|
||||
if s.Structural.Items == nil {
|
||||
return nil
|
||||
}
|
||||
return &Structural{Structural: s.Structural.Items}
|
||||
}
|
||||
|
||||
@@ -279,11 +282,18 @@ func nestedValueValidationToStructural(nvv *schema.NestedValueValidation) *Struc
|
||||
newProperties[k] = *nestedValueValidationToStructural(&v).Structural
|
||||
}
|
||||
|
||||
var newAdditionalProperties *schema.StructuralOrBool
|
||||
if nvv.AdditionalProperties != nil {
|
||||
newAdditionalProperties = &schema.StructuralOrBool{Structural: nestedValueValidationToStructural(nvv.AdditionalProperties).Structural}
|
||||
}
|
||||
|
||||
return &Structural{
|
||||
Structural: &schema.Structural{
|
||||
Items: newItems,
|
||||
Properties: newProperties,
|
||||
ValueValidation: &nvv.ValueValidation,
|
||||
Items: newItems,
|
||||
Properties: newProperties,
|
||||
AdditionalProperties: newAdditionalProperties,
|
||||
ValueValidation: &nvv.ValueValidation,
|
||||
ValidationExtensions: nvv.ValidationExtensions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
8
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go
generated
vendored
8
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go
generated
vendored
@@ -49,9 +49,11 @@ func WithTypeAndObjectMeta(s *schema.Structural) *schema.Structural {
|
||||
return s
|
||||
}
|
||||
result := &schema.Structural{
|
||||
Generic: s.Generic,
|
||||
Extensions: s.Extensions,
|
||||
ValueValidation: s.ValueValidation,
|
||||
AdditionalProperties: s.AdditionalProperties,
|
||||
Generic: s.Generic,
|
||||
Extensions: s.Extensions,
|
||||
ValueValidation: s.ValueValidation,
|
||||
ValidationExtensions: s.ValidationExtensions,
|
||||
}
|
||||
props := make(map[string]schema.Structural, len(s.Properties))
|
||||
for k, prop := range s.Properties {
|
||||
|
||||
236
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation.go
generated
vendored
236
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation.go
generated
vendored
@@ -31,9 +31,6 @@ import (
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
|
||||
@@ -45,6 +42,8 @@ import (
|
||||
"k8s.io/apiserver/pkg/cel/metrics"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/apiserver/pkg/warning"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
||||
)
|
||||
@@ -52,12 +51,15 @@ import (
|
||||
// Validator parallels the structure of schema.Structural and includes the compiled CEL programs
|
||||
// for the x-kubernetes-validations of each schema node.
|
||||
type Validator struct {
|
||||
Items *Validator
|
||||
Properties map[string]Validator
|
||||
|
||||
Items *Validator
|
||||
Properties map[string]Validator
|
||||
AllOfValidators []*Validator
|
||||
AdditionalProperties *Validator
|
||||
|
||||
compiledRules []CompilationResult
|
||||
Schema *schema.Structural
|
||||
|
||||
uncompiledRules []apiextensions.ValidationRule
|
||||
compiledRules []CompilationResult
|
||||
|
||||
// Program compilation is pre-checked at CRD creation/update time, so we don't expect compilation to fail
|
||||
// they are recompiled and added to this type, and it does, it is an internal bug.
|
||||
@@ -83,26 +85,47 @@ func NewValidator(s *schema.Structural, isResourceRoot bool, perCallLimit uint64
|
||||
if !hasXValidations(s) {
|
||||
return nil
|
||||
}
|
||||
return validator(s, isResourceRoot, model.SchemaDeclType(s, isResourceRoot), perCallLimit)
|
||||
return validator(s, s, isResourceRoot, model.SchemaDeclType(s, isResourceRoot), perCallLimit)
|
||||
}
|
||||
|
||||
// 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
|
||||
// exist. declType is expected to be a CEL DeclType corresponding to the structural schema.
|
||||
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit from k8s.io/apiserver/pkg/apis/cel/config.go as input.
|
||||
func validator(s *schema.Structural, isResourceRoot bool, declType *cel.DeclType, perCallLimit uint64) *Validator {
|
||||
compiledRules, err := Compile(s, declType, perCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()), StoredExpressionsEnvLoader())
|
||||
func validator(validationSchema, nodeSchema *schema.Structural, isResourceRoot bool, declType *cel.DeclType, perCallLimit uint64) *Validator {
|
||||
compilationSchema := *nodeSchema
|
||||
compilationSchema.XValidations = validationSchema.XValidations
|
||||
compiledRules, err := Compile(&compilationSchema, declType, perCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true), StoredExpressionsEnvLoader())
|
||||
|
||||
var itemsValidator, additionalPropertiesValidator *Validator
|
||||
var propertiesValidators map[string]Validator
|
||||
if s.Items != nil {
|
||||
itemsValidator = validator(s.Items, s.Items.XEmbeddedResource, declType.ElemType, perCallLimit)
|
||||
var allOfValidators []*Validator
|
||||
var elemType *cel.DeclType
|
||||
if declType != nil {
|
||||
elemType = declType.ElemType
|
||||
} else {
|
||||
elemType = declType
|
||||
}
|
||||
if len(s.Properties) > 0 {
|
||||
propertiesValidators = make(map[string]Validator, len(s.Properties))
|
||||
for k, p := range s.Properties {
|
||||
prop := p
|
||||
|
||||
if validationSchema.Items != nil && nodeSchema.Items != nil {
|
||||
itemsValidator = validator(validationSchema.Items, nodeSchema.Items, nodeSchema.Items.XEmbeddedResource, elemType, perCallLimit)
|
||||
}
|
||||
|
||||
if len(validationSchema.Properties) > 0 {
|
||||
propertiesValidators = make(map[string]Validator, len(validationSchema.Properties))
|
||||
for k, validationProperty := range validationSchema.Properties {
|
||||
nodeProperty, ok := nodeSchema.Properties[k]
|
||||
if !ok {
|
||||
// Can only add value validations for fields that are on the
|
||||
// structural spine of the schema.
|
||||
continue
|
||||
}
|
||||
|
||||
var fieldType *cel.DeclType
|
||||
if escapedPropName, ok := cel.Escape(k); ok {
|
||||
if declType == nil {
|
||||
continue
|
||||
}
|
||||
if f, ok := declType.Fields[escapedPropName]; ok {
|
||||
fieldType = f.Type
|
||||
} else {
|
||||
@@ -112,20 +135,32 @@ func validator(s *schema.Structural, isResourceRoot bool, declType *cel.DeclType
|
||||
} else {
|
||||
// field may be absent from declType if the property name is unescapable, in which case we should convert
|
||||
// the field value type to a DeclType.
|
||||
fieldType = model.SchemaDeclType(&prop, prop.XEmbeddedResource)
|
||||
fieldType = model.SchemaDeclType(&nodeProperty, nodeProperty.XEmbeddedResource)
|
||||
if fieldType == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if p := validator(&prop, prop.XEmbeddedResource, fieldType, perCallLimit); p != nil {
|
||||
if p := validator(&validationProperty, &nodeProperty, nodeProperty.XEmbeddedResource, fieldType, perCallLimit); p != nil {
|
||||
propertiesValidators[k] = *p
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil {
|
||||
additionalPropertiesValidator = validator(s.AdditionalProperties.Structural, s.AdditionalProperties.Structural.XEmbeddedResource, declType.ElemType, perCallLimit)
|
||||
if validationSchema.AdditionalProperties != nil && validationSchema.AdditionalProperties.Structural != nil &&
|
||||
nodeSchema.AdditionalProperties != nil && nodeSchema.AdditionalProperties.Structural != nil {
|
||||
additionalPropertiesValidator = validator(validationSchema.AdditionalProperties.Structural, nodeSchema.AdditionalProperties.Structural, nodeSchema.AdditionalProperties.Structural.XEmbeddedResource, elemType, perCallLimit)
|
||||
}
|
||||
if len(compiledRules) > 0 || err != nil || itemsValidator != nil || additionalPropertiesValidator != nil || len(propertiesValidators) > 0 {
|
||||
|
||||
if validationSchema.ValueValidation != nil && len(validationSchema.ValueValidation.AllOf) > 0 {
|
||||
allOfValidators = make([]*Validator, 0, len(validationSchema.ValueValidation.AllOf))
|
||||
for _, allOf := range validationSchema.ValueValidation.AllOf {
|
||||
allOfValidator := validator(nestedToStructural(&allOf), nodeSchema, isResourceRoot, declType, perCallLimit)
|
||||
if allOfValidator != nil {
|
||||
allOfValidators = append(allOfValidators, allOfValidator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(compiledRules) > 0 || err != nil || itemsValidator != nil || additionalPropertiesValidator != nil || len(propertiesValidators) > 0 || len(allOfValidators) > 0 {
|
||||
activationFactory := validationActivationWithoutOldSelf
|
||||
for _, rule := range compiledRules {
|
||||
if rule.UsesOldSelf {
|
||||
@@ -136,12 +171,15 @@ func validator(s *schema.Structural, isResourceRoot bool, declType *cel.DeclType
|
||||
|
||||
return &Validator{
|
||||
compiledRules: compiledRules,
|
||||
uncompiledRules: validationSchema.XValidations,
|
||||
compilationErr: err,
|
||||
isResourceRoot: isResourceRoot,
|
||||
Items: itemsValidator,
|
||||
AdditionalProperties: additionalPropertiesValidator,
|
||||
Properties: propertiesValidators,
|
||||
AllOfValidators: allOfValidators,
|
||||
celActivationFactory: activationFactory,
|
||||
Schema: nodeSchema,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,13 +202,13 @@ func WithRatcheting(correlation *common.CorrelatedObject) Option {
|
||||
// If the validation rules exceed the costBudget, subsequent evaluations will be skipped, the list of errs returned will not be empty, and a negative remainingBudget will be returned.
|
||||
// Most callers can ignore the returned remainingBudget value unless another validate call is going to be made
|
||||
// context is passed for supporting context cancellation during cel validation
|
||||
func (s *Validator) Validate(ctx context.Context, fldPath *field.Path, sts *schema.Structural, obj, oldObj interface{}, costBudget int64, opts ...Option) (errs field.ErrorList, remainingBudget int64) {
|
||||
func (s *Validator) Validate(ctx context.Context, fldPath *field.Path, _ *schema.Structural, obj, oldObj interface{}, costBudget int64, opts ...Option) (errs field.ErrorList, remainingBudget int64) {
|
||||
opt := options{}
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return s.validate(ctx, fldPath, sts, obj, oldObj, opt.ratchetingOptions, costBudget)
|
||||
return s.validate(ctx, fldPath, obj, oldObj, opt.ratchetingOptions, costBudget)
|
||||
}
|
||||
|
||||
// ratchetingOptions stores the current correlation object and the nearest
|
||||
@@ -210,8 +248,16 @@ func (r ratchetingOptions) shouldRatchetError() bool {
|
||||
func (r ratchetingOptions) key(field string) ratchetingOptions {
|
||||
if r.currentCorrelation == nil {
|
||||
return r
|
||||
} else if r.nearestParentCorrelation == nil && (field == "apiVersion" || field == "kind") {
|
||||
// We cannot ratchet changes to the APIVersion and kind fields field since
|
||||
// they aren't visible. (both old and new are converted to the same type)
|
||||
//
|
||||
return ratchetingOptions{}
|
||||
}
|
||||
|
||||
// nearestParentCorrelation is always non-nil except for the root node.
|
||||
// The below line ensures that the next nearestParentCorrelation is set
|
||||
// to a non-nil r.currentCorrelation
|
||||
return ratchetingOptions{currentCorrelation: r.currentCorrelation.Key(field), nearestParentCorrelation: r.currentCorrelation}
|
||||
}
|
||||
|
||||
@@ -226,7 +272,36 @@ func (r ratchetingOptions) index(idx int) ratchetingOptions {
|
||||
return ratchetingOptions{currentCorrelation: r.currentCorrelation.Index(idx), nearestParentCorrelation: r.currentCorrelation}
|
||||
}
|
||||
|
||||
func (s *Validator) validate(ctx context.Context, fldPath *field.Path, sts *schema.Structural, obj, oldObj interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
func nestedToStructural(nested *schema.NestedValueValidation) *schema.Structural {
|
||||
if nested == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
structuralConversion := &schema.Structural{
|
||||
ValueValidation: &nested.ValueValidation,
|
||||
ValidationExtensions: nested.ValidationExtensions,
|
||||
Generic: nested.ForbiddenGenerics,
|
||||
Extensions: nested.ForbiddenExtensions,
|
||||
Items: nestedToStructural(nested.Items),
|
||||
}
|
||||
|
||||
if len(nested.Properties) > 0 {
|
||||
structuralConversion.Properties = make(map[string]schema.Structural, len(nested.Properties))
|
||||
for k, v := range nested.Properties {
|
||||
structuralConversion.Properties[k] = *nestedToStructural(&v)
|
||||
}
|
||||
}
|
||||
|
||||
if nested.AdditionalProperties != nil {
|
||||
structuralConversion.AdditionalProperties = &schema.StructuralOrBool{
|
||||
Structural: nestedToStructural(nested.AdditionalProperties),
|
||||
}
|
||||
}
|
||||
|
||||
return structuralConversion
|
||||
}
|
||||
|
||||
func (s *Validator) validate(ctx context.Context, fldPath *field.Path, obj, oldObj interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
t := time.Now()
|
||||
defer func() {
|
||||
metrics.Metrics.ObserveEvaluation(time.Since(t))
|
||||
@@ -236,23 +311,37 @@ func (s *Validator) validate(ctx context.Context, fldPath *field.Path, sts *sche
|
||||
return nil, remainingBudget
|
||||
}
|
||||
|
||||
errs, remainingBudget = s.validateExpressions(ctx, fldPath, sts, obj, oldObj, correlation, remainingBudget)
|
||||
errs, remainingBudget = s.validateExpressions(ctx, fldPath, obj, oldObj, correlation, remainingBudget)
|
||||
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
}
|
||||
|
||||
// If the schema has allOf, recurse through those elements to see if there
|
||||
// are any validation rules that need to be evaluated.
|
||||
for _, allOfValidator := range s.AllOfValidators {
|
||||
var allOfErrs field.ErrorList
|
||||
// Pass options with nil currentCorrelation to mirror schema ratcheting
|
||||
// behavior which does not ratchet allOf errors. This may change in the
|
||||
// future for allOf.
|
||||
allOfErrs, remainingBudget = allOfValidator.validate(ctx, fldPath, obj, oldObj, ratchetingOptions{nearestParentCorrelation: correlation.nearestParentCorrelation}, remainingBudget)
|
||||
errs = append(errs, allOfErrs...)
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
}
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case []interface{}:
|
||||
oldArray, _ := oldObj.([]interface{})
|
||||
var arrayErrs field.ErrorList
|
||||
arrayErrs, remainingBudget = s.validateArray(ctx, fldPath, sts, obj, oldArray, correlation, remainingBudget)
|
||||
arrayErrs, remainingBudget = s.validateArray(ctx, fldPath, obj, oldArray, correlation, remainingBudget)
|
||||
errs = append(errs, arrayErrs...)
|
||||
return errs, remainingBudget
|
||||
case map[string]interface{}:
|
||||
oldMap, _ := oldObj.(map[string]interface{})
|
||||
var mapErrs field.ErrorList
|
||||
mapErrs, remainingBudget = s.validateMap(ctx, fldPath, sts, obj, oldMap, correlation, remainingBudget)
|
||||
mapErrs, remainingBudget = s.validateMap(ctx, fldPath, obj, oldMap, correlation, remainingBudget)
|
||||
errs = append(errs, mapErrs...)
|
||||
return errs, remainingBudget
|
||||
}
|
||||
@@ -260,7 +349,9 @@ func (s *Validator) validate(ctx context.Context, fldPath *field.Path, sts *sche
|
||||
return errs, remainingBudget
|
||||
}
|
||||
|
||||
func (s *Validator) validateExpressions(ctx context.Context, fldPath *field.Path, sts *schema.Structural, obj, oldObj interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
func (s *Validator) validateExpressions(ctx context.Context, fldPath *field.Path, obj, oldObj interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
sts := s.Schema
|
||||
|
||||
// guard against oldObj being a non-nil interface with a nil value
|
||||
if oldObj != nil {
|
||||
v := reflect.ValueOf(oldObj)
|
||||
@@ -295,7 +386,7 @@ func (s *Validator) validateExpressions(ctx context.Context, fldPath *field.Path
|
||||
}
|
||||
activation, optionalOldSelfActivation := s.celActivationFactory(sts, obj, oldObj)
|
||||
for i, compiled := range s.compiledRules {
|
||||
rule := sts.XValidations[i]
|
||||
rule := s.uncompiledRules[i]
|
||||
if compiled.Error != nil {
|
||||
errs = append(errs, field.Invalid(fldPath, sts.Type, fmt.Sprintf("rule compile error: %v", compiled.Error)))
|
||||
continue
|
||||
@@ -361,8 +452,9 @@ func (s *Validator) validateExpressions(ctx context.Context, fldPath *field.Path
|
||||
continue
|
||||
}
|
||||
if evalResult != types.True {
|
||||
currentFldPath := fldPath
|
||||
if len(compiled.NormalizedRuleFieldPath) > 0 {
|
||||
fldPath = fldPath.Child(compiled.NormalizedRuleFieldPath)
|
||||
currentFldPath = currentFldPath.Child(compiled.NormalizedRuleFieldPath)
|
||||
}
|
||||
|
||||
addErr := func(e *field.Error) {
|
||||
@@ -377,22 +469,22 @@ func (s *Validator) validateExpressions(ctx context.Context, fldPath *field.Path
|
||||
messageExpression, newRemainingBudget, msgErr := evalMessageExpression(ctx, compiled.MessageExpression, rule.MessageExpression, activation, remainingBudget)
|
||||
if msgErr != nil {
|
||||
if msgErr.Type == cel.ErrorTypeInternal {
|
||||
addErr(field.InternalError(fldPath, msgErr))
|
||||
addErr(field.InternalError(currentFldPath, msgErr))
|
||||
return errs, -1
|
||||
} else if msgErr.Type == cel.ErrorTypeInvalid {
|
||||
addErr(field.Invalid(fldPath, sts.Type, msgErr.Error()))
|
||||
addErr(field.Invalid(currentFldPath, sts.Type, msgErr.Error()))
|
||||
return errs, -1
|
||||
} else {
|
||||
klog.V(2).ErrorS(msgErr, "messageExpression evaluation failed")
|
||||
addErr(fieldErrorForReason(fldPath, sts.Type, ruleMessageOrDefault(rule), rule.Reason))
|
||||
addErr(fieldErrorForReason(currentFldPath, sts.Type, ruleMessageOrDefault(rule), rule.Reason))
|
||||
remainingBudget = newRemainingBudget
|
||||
}
|
||||
} else {
|
||||
addErr(fieldErrorForReason(fldPath, sts.Type, messageExpression, rule.Reason))
|
||||
addErr(fieldErrorForReason(currentFldPath, sts.Type, messageExpression, rule.Reason))
|
||||
remainingBudget = newRemainingBudget
|
||||
}
|
||||
} else {
|
||||
addErr(fieldErrorForReason(fldPath, sts.Type, ruleMessageOrDefault(rule), rule.Reason))
|
||||
addErr(fieldErrorForReason(currentFldPath, sts.Type, ruleMessageOrDefault(rule), rule.Reason))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,9 +524,30 @@ func unescapeSingleQuote(s string) (string, error) {
|
||||
return unescaped, err
|
||||
}
|
||||
|
||||
type validFieldPathOptions struct {
|
||||
allowArrayNotation bool
|
||||
}
|
||||
|
||||
// ValidFieldPathOption provides vararg options for ValidFieldPath.
|
||||
type ValidFieldPathOption func(*validFieldPathOptions)
|
||||
|
||||
// WithFieldPathAllowArrayNotation sets of array annotation ('[<index or map key>]') is allowed
|
||||
// in field paths.
|
||||
// Defaults to true
|
||||
func WithFieldPathAllowArrayNotation(allow bool) ValidFieldPathOption {
|
||||
return func(options *validFieldPathOptions) {
|
||||
options.allowArrayNotation = allow
|
||||
}
|
||||
}
|
||||
|
||||
// ValidFieldPath validates that jsonPath is a valid JSON Path containing only field and map accessors
|
||||
// that are valid for the given schema, and returns a field.Path representation of the validated jsonPath or an error.
|
||||
func ValidFieldPath(jsonPath string, schema *schema.Structural) (validFieldPath *field.Path, err error) {
|
||||
func ValidFieldPath(jsonPath string, schema *schema.Structural, options ...ValidFieldPathOption) (validFieldPath *field.Path, foundSchema *schema.Structural, err error) {
|
||||
opts := &validFieldPathOptions{allowArrayNotation: true}
|
||||
for _, opt := range options {
|
||||
opt(opts)
|
||||
}
|
||||
|
||||
appendToPath := func(name string, isNamed bool) error {
|
||||
if !isNamed {
|
||||
validFieldPath = validFieldPath.Key(name)
|
||||
@@ -495,16 +608,19 @@ func ValidFieldPath(jsonPath string, schema *schema.Structural) (validFieldPath
|
||||
tok = scanner.Text()
|
||||
switch tok {
|
||||
case "[":
|
||||
if !opts.allowArrayNotation {
|
||||
return nil, nil, fmt.Errorf("array notation is not allowed")
|
||||
}
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("unexpected end of JSON path")
|
||||
return nil, nil, fmt.Errorf("unexpected end of JSON path")
|
||||
}
|
||||
tok = scanner.Text()
|
||||
if len(tok) < 2 || tok[0] != '\'' || tok[len(tok)-1] != '\'' {
|
||||
return nil, fmt.Errorf("expected single quoted string but got %s", tok)
|
||||
return nil, nil, fmt.Errorf("expected single quoted string but got %s", tok)
|
||||
}
|
||||
unescaped, err := unescapeSingleQuote(tok[1 : len(tok)-1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid string literal: %v", err)
|
||||
return nil, nil, fmt.Errorf("invalid string literal: %w", err)
|
||||
}
|
||||
|
||||
if schema.Properties != nil {
|
||||
@@ -512,21 +628,21 @@ func ValidFieldPath(jsonPath string, schema *schema.Structural) (validFieldPath
|
||||
} else if schema.AdditionalProperties != nil {
|
||||
isNamed = false
|
||||
} else {
|
||||
return nil, fmt.Errorf("does not refer to a valid field")
|
||||
return nil, nil, fmt.Errorf("does not refer to a valid field")
|
||||
}
|
||||
if err := appendToPath(unescaped, isNamed); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("unexpected end of JSON path")
|
||||
return nil, nil, fmt.Errorf("unexpected end of JSON path")
|
||||
}
|
||||
tok = scanner.Text()
|
||||
if tok != "]" {
|
||||
return nil, fmt.Errorf("expected ] but got %s", tok)
|
||||
return nil, nil, fmt.Errorf("expected ] but got %s", tok)
|
||||
}
|
||||
case ".":
|
||||
if !scanner.Scan() {
|
||||
return nil, fmt.Errorf("unexpected end of JSON path")
|
||||
return nil, nil, fmt.Errorf("unexpected end of JSON path")
|
||||
}
|
||||
tok = scanner.Text()
|
||||
if schema.Properties != nil {
|
||||
@@ -534,16 +650,17 @@ func ValidFieldPath(jsonPath string, schema *schema.Structural) (validFieldPath
|
||||
} else if schema.AdditionalProperties != nil {
|
||||
isNamed = false
|
||||
} else {
|
||||
return nil, fmt.Errorf("does not refer to a valid field")
|
||||
return nil, nil, fmt.Errorf("does not refer to a valid field")
|
||||
}
|
||||
if err := appendToPath(tok, isNamed); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("expected [ or . but got: %s", tok)
|
||||
return nil, nil, fmt.Errorf("expected [ or . but got: %s", tok)
|
||||
}
|
||||
}
|
||||
return validFieldPath, nil
|
||||
|
||||
return validFieldPath, schema, nil
|
||||
}
|
||||
|
||||
func fieldErrorForReason(fldPath *field.Path, value interface{}, detail string, reason *apiextensions.FieldValueErrorReason) *field.Error {
|
||||
@@ -686,7 +803,7 @@ func (a *validationActivation) Parent() interpreter.Activation {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Validator) validateMap(ctx context.Context, fldPath *field.Path, sts *schema.Structural, obj, oldObj map[string]interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
func (s *Validator) validateMap(ctx context.Context, fldPath *field.Path, obj, oldObj map[string]interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
remainingBudget = costBudget
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
@@ -695,9 +812,9 @@ func (s *Validator) validateMap(ctx context.Context, fldPath *field.Path, sts *s
|
||||
return nil, remainingBudget
|
||||
}
|
||||
|
||||
correlatable := MapIsCorrelatable(sts.XMapType)
|
||||
correlatable := MapIsCorrelatable(s.Schema.XMapType)
|
||||
|
||||
if s.AdditionalProperties != nil && sts.AdditionalProperties != nil && sts.AdditionalProperties.Structural != nil {
|
||||
if s.AdditionalProperties != nil {
|
||||
for k, v := range obj {
|
||||
var oldV interface{}
|
||||
if correlatable {
|
||||
@@ -705,25 +822,24 @@ func (s *Validator) validateMap(ctx context.Context, fldPath *field.Path, sts *s
|
||||
}
|
||||
|
||||
var err field.ErrorList
|
||||
err, remainingBudget = s.AdditionalProperties.validate(ctx, fldPath.Key(k), sts.AdditionalProperties.Structural, v, oldV, correlation.key(k), remainingBudget)
|
||||
err, remainingBudget = s.AdditionalProperties.validate(ctx, fldPath.Key(k), v, oldV, correlation.key(k), remainingBudget)
|
||||
errs = append(errs, err...)
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.Properties != nil && sts.Properties != nil {
|
||||
if s.Properties != nil {
|
||||
for k, v := range obj {
|
||||
stsProp, stsOk := sts.Properties[k]
|
||||
sub, ok := s.Properties[k]
|
||||
if ok && stsOk {
|
||||
if ok {
|
||||
var oldV interface{}
|
||||
if correlatable {
|
||||
oldV = oldObj[k] // +k8s:verify-mutation:reason=clone
|
||||
}
|
||||
|
||||
var err field.ErrorList
|
||||
err, remainingBudget = sub.validate(ctx, fldPath.Child(k), &stsProp, v, oldV, correlation.key(k), remainingBudget)
|
||||
err, remainingBudget = sub.validate(ctx, fldPath.Child(k), v, oldV, correlation.key(k), remainingBudget)
|
||||
errs = append(errs, err...)
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
@@ -735,19 +851,19 @@ func (s *Validator) validateMap(ctx context.Context, fldPath *field.Path, sts *s
|
||||
return errs, remainingBudget
|
||||
}
|
||||
|
||||
func (s *Validator) validateArray(ctx context.Context, fldPath *field.Path, sts *schema.Structural, obj, oldObj []interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
func (s *Validator) validateArray(ctx context.Context, fldPath *field.Path, obj, oldObj []interface{}, correlation ratchetingOptions, costBudget int64) (errs field.ErrorList, remainingBudget int64) {
|
||||
remainingBudget = costBudget
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
}
|
||||
|
||||
if s.Items != nil && sts.Items != nil {
|
||||
if s.Items != nil {
|
||||
// only map-type lists support self-oldSelf correlation for cel rules. if this isn't a
|
||||
// map-type list, then makeMapList returns an implementation that always returns nil
|
||||
correlatableOldItems := makeMapList(sts, oldObj)
|
||||
correlatableOldItems := makeMapList(s.Schema, oldObj)
|
||||
for i := range obj {
|
||||
var err field.ErrorList
|
||||
err, remainingBudget = s.Items.validate(ctx, fldPath.Index(i), sts.Items, obj[i], correlatableOldItems.Get(obj[i]), correlation.index(i), remainingBudget)
|
||||
err, remainingBudget = s.Items.validate(ctx, fldPath.Index(i), obj[i], correlatableOldItems.Get(obj[i]), correlation.index(i), remainingBudget)
|
||||
errs = append(errs, err...)
|
||||
if remainingBudget < 0 {
|
||||
return errs, remainingBudget
|
||||
|
||||
54
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/complete.go
generated
vendored
54
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/complete.go
generated
vendored
@@ -22,17 +22,24 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// validateStructuralCompleteness checks that every specified field or array in s is also specified
|
||||
// outside of value validation.
|
||||
func validateStructuralCompleteness(s *Structural, fldPath *field.Path) field.ErrorList {
|
||||
// validateStructuralCompleteness checks that all value validations in s have
|
||||
// a structural counterpart so that every value validation applies to a value
|
||||
// with a known schema:
|
||||
// - validations for specific properties must have that property (or additionalProperties under an option) structurally defined
|
||||
// - additionalProperties validations must have additionalProperties defined in the structural portion of the schema corresponding to that node
|
||||
// - Items validations must have also have a corresponding items structurally
|
||||
//
|
||||
// The "structural" portion of the schema refers to all nodes in the
|
||||
// schema traversible without following any NestedValueValidations.
|
||||
func validateStructuralCompleteness(s *Structural, fldPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return validateValueValidationCompleteness(s.ValueValidation, s, fldPath, fldPath)
|
||||
return validateValueValidationCompleteness(s.ValueValidation, s, fldPath, fldPath, opts)
|
||||
}
|
||||
|
||||
func validateValueValidationCompleteness(v *ValueValidation, s *Structural, sPath, vPath *field.Path) field.ErrorList {
|
||||
func validateValueValidationCompleteness(v *ValueValidation, s *Structural, sPath, vPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -42,21 +49,21 @@ func validateValueValidationCompleteness(v *ValueValidation, s *Structural, sPat
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Not, s, sPath, vPath.Child("not"))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Not, s, sPath, vPath.Child("not"), opts)...)
|
||||
for i := range v.AllOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AllOf[i], s, sPath, vPath.Child("allOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AllOf[i], s, sPath, vPath.Child("allOf").Index(i), opts)...)
|
||||
}
|
||||
for i := range v.AnyOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AnyOf[i], s, sPath, vPath.Child("anyOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AnyOf[i], s, sPath, vPath.Child("anyOf").Index(i), opts)...)
|
||||
}
|
||||
for i := range v.OneOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.OneOf[i], s, sPath, vPath.Child("oneOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.OneOf[i], s, sPath, vPath.Child("oneOf").Index(i), opts)...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateNestedValueValidationCompleteness(v *NestedValueValidation, s *Structural, sPath, vPath *field.Path) field.ErrorList {
|
||||
func validateNestedValueValidationCompleteness(v *NestedValueValidation, s *Structural, sPath, vPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -66,17 +73,34 @@ func validateNestedValueValidationCompleteness(v *NestedValueValidation, s *Stru
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateValueValidationCompleteness(&v.ValueValidation, s, sPath, vPath)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Items, s.Items, sPath.Child("items"), vPath.Child("items"))...)
|
||||
allErrs = append(allErrs, validateValueValidationCompleteness(&v.ValueValidation, s, sPath, vPath, opts)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Items, s.Items, sPath.Child("items"), vPath.Child("items"), opts)...)
|
||||
|
||||
var sAdditionalPropertiesSchema *Structural
|
||||
if s.AdditionalProperties != nil {
|
||||
sAdditionalPropertiesSchema = s.AdditionalProperties.Structural
|
||||
}
|
||||
|
||||
for k, vFld := range v.Properties {
|
||||
if sFld, ok := s.Properties[k]; !ok {
|
||||
allErrs = append(allErrs, field.Required(sPath.Child("properties").Key(k), fmt.Sprintf("because it is defined in %s", vPath.Child("properties").Key(k))))
|
||||
if sAdditionalPropertiesSchema == nil || !opts.AllowValidationPropertiesWithAdditionalProperties {
|
||||
allErrs = append(allErrs, field.Required(sPath.Child("properties").Key(k), fmt.Sprintf("because it is defined in %s", vPath.Child("properties").Key(k))))
|
||||
} else {
|
||||
// Allow validations on specific properties if there exists an
|
||||
// additionalProperties structural schema specified instead of
|
||||
// direct properties
|
||||
// NOTE: This does not allow `additionalProperties: true` structural
|
||||
// schema to be combined with specific property validations.
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&vFld, sAdditionalPropertiesSchema, sPath.Child("additionalProperties"), vPath.Child("properties").Key(k), opts)...)
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&vFld, &sFld, sPath.Child("properties").Key(k), vPath.Child("properties").Key(k))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&vFld, &sFld, sPath.Child("properties").Key(k), vPath.Child("properties").Key(k), opts)...)
|
||||
}
|
||||
}
|
||||
|
||||
// don't check additionalProperties as this is not allowed (and checked during validation)
|
||||
if v.AdditionalProperties != nil && opts.AllowNestedAdditionalProperties {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.AdditionalProperties, sAdditionalPropertiesSchema, sPath.Child("additionalProperties"), vPath.Child("additionalProperties"), opts)...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
77
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go
generated
vendored
77
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go
generated
vendored
@@ -18,6 +18,7 @@ package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
)
|
||||
@@ -62,10 +63,16 @@ func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vx, err := newValidationExtensions(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ss := &Structural{
|
||||
Generic: *g,
|
||||
Extensions: *x,
|
||||
ValueValidation: vv,
|
||||
Generic: *g,
|
||||
Extensions: *x,
|
||||
ValueValidation: vv,
|
||||
ValidationExtensions: *vx,
|
||||
}
|
||||
|
||||
if s.Items != nil {
|
||||
@@ -91,6 +98,18 @@ func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if s.AdditionalProperties != nil {
|
||||
if s.AdditionalProperties.Schema != nil {
|
||||
additionalPropertiesSchema, err := NewStructural(s.AdditionalProperties.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ss.AdditionalProperties = &StructuralOrBool{Structural: additionalPropertiesSchema, Bool: true}
|
||||
} else {
|
||||
ss.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows}
|
||||
}
|
||||
}
|
||||
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
@@ -108,18 +127,6 @@ func newGenerics(s *apiextensions.JSONSchemaProps) (*Generic, error) {
|
||||
g.Default = JSON{interface{}(*s.Default)}
|
||||
}
|
||||
|
||||
if s.AdditionalProperties != nil {
|
||||
if s.AdditionalProperties.Schema != nil {
|
||||
ss, err := NewStructural(s.AdditionalProperties.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.AdditionalProperties = &StructuralOrBool{Structural: ss, Bool: true}
|
||||
} else {
|
||||
g.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows}
|
||||
}
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
@@ -205,10 +212,16 @@ func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueVal
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vx, err := newValidationExtensions(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &NestedValueValidation{
|
||||
ValueValidation: *vv,
|
||||
ForbiddenGenerics: *g,
|
||||
ForbiddenExtensions: *x,
|
||||
ValueValidation: *vv,
|
||||
ValidationExtensions: *vx,
|
||||
ForbiddenGenerics: *g,
|
||||
ForbiddenExtensions: *x,
|
||||
}
|
||||
|
||||
if s.Items != nil {
|
||||
@@ -232,6 +245,18 @@ func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueVal
|
||||
v.Properties[k] = *nvv
|
||||
}
|
||||
}
|
||||
if s.AdditionalProperties != nil {
|
||||
if s.AdditionalProperties.Schema != nil {
|
||||
additionalPropertiesSchema, err := newNestedValueValidation(s.AdditionalProperties.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.AdditionalProperties = additionalPropertiesSchema
|
||||
} else if s.AdditionalProperties.Allows {
|
||||
v.AdditionalProperties = &NestedValueValidation{}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
@@ -248,9 +273,6 @@ func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) {
|
||||
XListType: s.XListType,
|
||||
XMapType: s.XMapType,
|
||||
}
|
||||
if err := apiextensionsv1.Convert_apiextensions_ValidationRules_To_v1_ValidationRules(&s.XValidations, &ret.XValidations, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.XPreserveUnknownFields != nil {
|
||||
if !*s.XPreserveUnknownFields {
|
||||
@@ -262,6 +284,19 @@ func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func newValidationExtensions(s *apiextensions.JSONSchemaProps) (*ValidationExtensions, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ret := &ValidationExtensions{}
|
||||
if err := apiextensionsv1.Convert_apiextensions_ValidationRules_To_v1_ValidationRules(&s.XValidations, &ret.XValidations, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// validateUnsupportedFields checks that those fields rejected by validation are actually unset.
|
||||
func validateUnsupportedFields(s *apiextensions.JSONSchemaProps) error {
|
||||
if len(s.ID) > 0 {
|
||||
|
||||
23
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go
generated
vendored
23
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go
generated
vendored
@@ -37,10 +37,16 @@ func (s *Structural) ToKubeOpenAPI() *spec.Schema {
|
||||
ret.Properties[k] = *v.ToKubeOpenAPI()
|
||||
}
|
||||
}
|
||||
if s.AdditionalProperties != nil {
|
||||
ret.AdditionalProperties = &spec.SchemaOrBool{
|
||||
Allows: s.AdditionalProperties.Bool,
|
||||
Schema: s.AdditionalProperties.Structural.ToKubeOpenAPI(),
|
||||
}
|
||||
}
|
||||
s.Generic.toKubeOpenAPI(ret)
|
||||
s.Extensions.toKubeOpenAPI(ret)
|
||||
s.ValueValidation.toKubeOpenAPI(ret)
|
||||
|
||||
s.ValidationExtensions.toKubeOpenAPI(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -53,12 +59,6 @@ func (g *Generic) toKubeOpenAPI(ret *spec.Schema) {
|
||||
ret.Type = spec.StringOrArray{g.Type}
|
||||
}
|
||||
ret.Nullable = g.Nullable
|
||||
if g.AdditionalProperties != nil {
|
||||
ret.AdditionalProperties = &spec.SchemaOrBool{
|
||||
Allows: g.AdditionalProperties.Bool,
|
||||
Schema: g.AdditionalProperties.Structural.ToKubeOpenAPI(),
|
||||
}
|
||||
}
|
||||
ret.Description = g.Description
|
||||
ret.Title = g.Title
|
||||
ret.Default = g.Default.Object
|
||||
@@ -87,6 +87,13 @@ func (x *Extensions) toKubeOpenAPI(ret *spec.Schema) {
|
||||
if x.XMapType != nil {
|
||||
ret.VendorExtensible.AddExtension("x-kubernetes-map-type", *x.XMapType)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ValidationExtensions) toKubeOpenAPI(ret *spec.Schema) {
|
||||
if x == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(x.XValidations) > 0 {
|
||||
ret.VendorExtensible.AddExtension("x-kubernetes-validations", x.XValidations)
|
||||
}
|
||||
@@ -138,6 +145,7 @@ func (vv *NestedValueValidation) toKubeOpenAPI() *spec.Schema {
|
||||
ret := &spec.Schema{}
|
||||
|
||||
vv.ValueValidation.toKubeOpenAPI(ret)
|
||||
vv.ValidationExtensions.toKubeOpenAPI(ret)
|
||||
if vv.Items != nil {
|
||||
ret.Items = &spec.SchemaOrArray{Schema: vv.Items.toKubeOpenAPI()}
|
||||
}
|
||||
@@ -149,6 +157,5 @@ func (vv *NestedValueValidation) toKubeOpenAPI() *spec.Schema {
|
||||
}
|
||||
vv.ForbiddenGenerics.toKubeOpenAPI(ret) // normally empty. Exception: int-or-string
|
||||
vv.ForbiddenExtensions.toKubeOpenAPI(ret) // shouldn't do anything
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
27
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go
generated
vendored
27
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go
generated
vendored
@@ -25,11 +25,13 @@ import (
|
||||
|
||||
// Structural represents a structural schema.
|
||||
type Structural struct {
|
||||
Items *Structural
|
||||
Properties map[string]Structural
|
||||
Items *Structural
|
||||
Properties map[string]Structural
|
||||
AdditionalProperties *StructuralOrBool
|
||||
|
||||
Generic
|
||||
Extensions
|
||||
ValidationExtensions
|
||||
|
||||
ValueValidation *ValueValidation
|
||||
}
|
||||
@@ -51,11 +53,10 @@ type Generic struct {
|
||||
// It can be object, array, number, integer, boolean, string.
|
||||
// It is optional only if x-kubernetes-preserve-unknown-fields
|
||||
// or x-kubernetes-int-or-string is true.
|
||||
Type string
|
||||
Title string
|
||||
Default JSON
|
||||
AdditionalProperties *StructuralOrBool
|
||||
Nullable bool
|
||||
Type string
|
||||
Title string
|
||||
Default JSON
|
||||
Nullable bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
@@ -128,7 +129,13 @@ type Extensions struct {
|
||||
// Atomic maps will be entirely replaced when updated.
|
||||
// +optional
|
||||
XMapType *string
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ValidationExtensions contains the Kubernetes OpenAPI v3 extensions that are
|
||||
// used for validation rather than structure.
|
||||
type ValidationExtensions struct {
|
||||
// x-kubernetes-validations describes a list of validation rules for expression validation.
|
||||
// Use the v1 struct since this gets serialized as an extension.
|
||||
XValidations apiextensionsv1.ValidationRules
|
||||
@@ -166,9 +173,11 @@ type ValueValidation struct {
|
||||
// under a logical junctor, and catch all structs for generic and vendor extensions schema fields.
|
||||
type NestedValueValidation struct {
|
||||
ValueValidation
|
||||
ValidationExtensions
|
||||
|
||||
Items *NestedValueValidation
|
||||
Properties map[string]NestedValueValidation
|
||||
Items *NestedValueValidation
|
||||
Properties map[string]NestedValueValidation
|
||||
AdditionalProperties *NestedValueValidation
|
||||
|
||||
// Anything set in the following will make the scheme
|
||||
// non-structural, with the exception of these two patterns if
|
||||
|
||||
93
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go
generated
vendored
93
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go
generated
vendored
@@ -42,6 +42,22 @@ const (
|
||||
fieldLevel
|
||||
)
|
||||
|
||||
type ValidationOptions struct {
|
||||
// AllowNestedAdditionalProperties allows additionalProperties to be specified in
|
||||
// nested contexts (allOf, anyOf, oneOf, not).
|
||||
AllowNestedAdditionalProperties bool
|
||||
|
||||
// AllowNestedXValidations allows x-kubernetes-validations to be specified in
|
||||
// nested contexts (allOf, anyOf, oneOf, not).
|
||||
AllowNestedXValidations bool
|
||||
|
||||
// AllowValidationPropertiesWithAdditionalProperties allows
|
||||
// value validations on specific properties that are structually
|
||||
// defined by additionalProperties. i.e. additionalProperties in main structural
|
||||
// schema, but properties within an allOf.
|
||||
AllowValidationPropertiesWithAdditionalProperties bool
|
||||
}
|
||||
|
||||
// ValidateStructural checks that s is a structural schema with the invariants:
|
||||
//
|
||||
// * structurality: both `ForbiddenGenerics` and `ForbiddenExtensions` only have zero values, with the two exceptions for IntOrString.
|
||||
@@ -61,10 +77,21 @@ const (
|
||||
// * metadata at the root can only restrict the name and generateName, and not be specified at all in nested contexts.
|
||||
// * additionalProperties at the root is not allowed.
|
||||
func ValidateStructural(fldPath *field.Path, s *Structural) field.ErrorList {
|
||||
return ValidateStructuralWithOptions(fldPath, s, ValidationOptions{
|
||||
// This would widen the schema for CRD if set to true, so first few releases will still
|
||||
// not admit any. But it can still be used by libraries and
|
||||
// declarative validation for native types
|
||||
AllowNestedAdditionalProperties: false,
|
||||
AllowNestedXValidations: false,
|
||||
AllowValidationPropertiesWithAdditionalProperties: false,
|
||||
})
|
||||
}
|
||||
|
||||
func ValidateStructuralWithOptions(fldPath *field.Path, s *Structural, opts ValidationOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s, rootLevel, fldPath)...)
|
||||
allErrs = append(allErrs, validateStructuralCompleteness(s, fldPath)...)
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s, rootLevel, fldPath, opts)...)
|
||||
allErrs = append(allErrs, validateStructuralCompleteness(s, fldPath, opts)...)
|
||||
|
||||
// sort error messages. Otherwise, the errors slice will change every time due to
|
||||
// maps in the types and randomized iteration.
|
||||
@@ -76,7 +103,7 @@ func ValidateStructural(fldPath *field.Path, s *Structural) field.ErrorList {
|
||||
}
|
||||
|
||||
// validateStructuralInvariants checks the invariants of a structural schema.
|
||||
func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path) field.ErrorList {
|
||||
func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -86,11 +113,21 @@ func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path)
|
||||
if s.Type == "array" && s.Items == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("items"), "must be specified"))
|
||||
}
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s.Items, itemLevel, fldPath.Child("items"))...)
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s.Items, itemLevel, fldPath.Child("items"), opts)...)
|
||||
|
||||
for k, v := range s.Properties {
|
||||
allErrs = append(allErrs, validateStructuralInvariants(&v, fieldLevel, fldPath.Child("properties").Key(k))...)
|
||||
allErrs = append(allErrs, validateStructuralInvariants(&v, fieldLevel, fldPath.Child("properties").Key(k), opts)...)
|
||||
}
|
||||
|
||||
if s.AdditionalProperties != nil {
|
||||
if lvl == rootLevel {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must not be used at the root"))
|
||||
}
|
||||
if s.AdditionalProperties.Structural != nil {
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s.AdditionalProperties.Structural, fieldLevel, fldPath.Child("additionalProperties"), opts)...)
|
||||
}
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateGeneric(&s.Generic, lvl, fldPath)...)
|
||||
allErrs = append(allErrs, validateExtensions(&s.Extensions, fldPath)...)
|
||||
|
||||
@@ -106,7 +143,7 @@ func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path)
|
||||
skipAnyOf := isIntOrStringAnyOfPattern(s)
|
||||
skipFirstAllOfAnyOf := isIntOrStringAllOfPattern(s)
|
||||
|
||||
allErrs = append(allErrs, validateValueValidation(s.ValueValidation, skipAnyOf, skipFirstAllOfAnyOf, lvl, fldPath)...)
|
||||
allErrs = append(allErrs, validateValueValidation(s.ValueValidation, skipAnyOf, skipFirstAllOfAnyOf, lvl, fldPath, opts)...)
|
||||
|
||||
checkMetadata := (lvl == rootLevel) || s.XEmbeddedResource
|
||||
|
||||
@@ -207,18 +244,7 @@ func validateGeneric(g *Generic, lvl level, fldPath *field.Path) field.ErrorList
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if g.AdditionalProperties != nil {
|
||||
if lvl == rootLevel {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must not be used at the root"))
|
||||
}
|
||||
if g.AdditionalProperties.Structural != nil {
|
||||
allErrs = append(allErrs, validateStructuralInvariants(g.AdditionalProperties.Structural, fieldLevel, fldPath.Child("additionalProperties"))...)
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateExtensions checks Kubernetes vendor extensions of a structural schema.
|
||||
@@ -236,16 +262,23 @@ func validateExtensions(x *Extensions, fldPath *field.Path) field.ErrorList {
|
||||
}
|
||||
|
||||
// validateValueValidation checks the value validation in a structural schema.
|
||||
func validateValueValidation(v *ValueValidation, skipAnyOf, skipFirstAllOfAnyOf bool, lvl level, fldPath *field.Path) field.ErrorList {
|
||||
func validateValueValidation(v *ValueValidation, skipAnyOf, skipFirstAllOfAnyOf bool, lvl level, fldPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
// We still unconditionally forbid XValidations in quantifiers, the only
|
||||
// quantifier that is allowed to have right now is AllOf. This is due to
|
||||
// implementation constraints - the SchemaValidator would need to become
|
||||
// aware of CEL to properly implement the other quantifiers.
|
||||
optsWithCELDisabled := opts
|
||||
optsWithCELDisabled.AllowNestedXValidations = false
|
||||
|
||||
if !skipAnyOf {
|
||||
for i := range v.AnyOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AnyOf[i], false, false, lvl, fldPath.Child("anyOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AnyOf[i], false, false, lvl, fldPath.Child("anyOf").Index(i), optsWithCELDisabled)...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,14 +287,14 @@ func validateValueValidation(v *ValueValidation, skipAnyOf, skipFirstAllOfAnyOf
|
||||
if skipFirstAllOfAnyOf && i == 0 {
|
||||
skipAnyOf = true
|
||||
}
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AllOf[i], skipAnyOf, false, lvl, fldPath.Child("allOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AllOf[i], skipAnyOf, false, lvl, fldPath.Child("allOf").Index(i), opts)...)
|
||||
}
|
||||
|
||||
for i := range v.OneOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.OneOf[i], false, false, lvl, fldPath.Child("oneOf").Index(i))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.OneOf[i], false, false, lvl, fldPath.Child("oneOf").Index(i), optsWithCELDisabled)...)
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Not, false, false, lvl, fldPath.Child("not"))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Not, false, false, lvl, fldPath.Child("not"), optsWithCELDisabled)...)
|
||||
|
||||
if len(v.Pattern) > 0 {
|
||||
if _, err := regexp.Compile(v.Pattern); err != nil {
|
||||
@@ -273,25 +306,27 @@ func validateValueValidation(v *ValueValidation, skipAnyOf, skipFirstAllOfAnyOf
|
||||
}
|
||||
|
||||
// validateNestedValueValidation checks the nested value validation under a logic junctor in a structural schema.
|
||||
func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllOfAnyOf bool, lvl level, fldPath *field.Path) field.ErrorList {
|
||||
func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllOfAnyOf bool, lvl level, fldPath *field.Path, opts ValidationOptions) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateValueValidation(&v.ValueValidation, skipAnyOf, skipAllOfAnyOf, lvl, fldPath)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Items, false, false, lvl, fldPath.Child("items"))...)
|
||||
allErrs = append(allErrs, validateValueValidation(&v.ValueValidation, skipAnyOf, skipAllOfAnyOf, lvl, fldPath, opts)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Items, false, false, lvl, fldPath.Child("items"), opts)...)
|
||||
|
||||
for k, fld := range v.Properties {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&fld, false, false, fieldLevel, fldPath.Child("properties").Key(k))...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&fld, false, false, fieldLevel, fldPath.Child("properties").Key(k), opts)...)
|
||||
}
|
||||
|
||||
if len(v.ForbiddenGenerics.Type) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("type"), "must be empty to be structural"))
|
||||
}
|
||||
if v.ForbiddenGenerics.AdditionalProperties != nil {
|
||||
if v.AdditionalProperties != nil && !opts.AllowNestedAdditionalProperties {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must be undefined to be structural"))
|
||||
} else {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.AdditionalProperties, false, false, lvl, fldPath.Child("additionalProperties"), opts)...)
|
||||
}
|
||||
if v.ForbiddenGenerics.Default.Object != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("default"), "must be undefined to be structural"))
|
||||
@@ -324,7 +359,7 @@ func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllO
|
||||
if v.ForbiddenExtensions.XMapType != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-map-type"), "must be undefined to be structural"))
|
||||
}
|
||||
if len(v.ForbiddenExtensions.XValidations) > 0 {
|
||||
if len(v.ValidationExtensions.XValidations) > 0 && !opts.AllowNestedXValidations {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-validations"), "must be empty to be structural"))
|
||||
}
|
||||
|
||||
|
||||
8
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go
generated
vendored
8
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go
generated
vendored
@@ -50,8 +50,8 @@ func (m *Visitor) visitStructural(s *Structural) bool {
|
||||
s.Properties[k] = v
|
||||
}
|
||||
}
|
||||
if s.Generic.AdditionalProperties != nil && s.Generic.AdditionalProperties.Structural != nil {
|
||||
m.visitStructural(s.Generic.AdditionalProperties.Structural)
|
||||
if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil {
|
||||
m.visitStructural(s.AdditionalProperties.Structural)
|
||||
}
|
||||
if s.ValueValidation != nil {
|
||||
for i := range s.ValueValidation.AllOf {
|
||||
@@ -86,8 +86,8 @@ func (m *Visitor) visitNestedValueValidation(vv *NestedValueValidation) bool {
|
||||
vv.Properties[k] = v
|
||||
}
|
||||
}
|
||||
if vv.ForbiddenGenerics.AdditionalProperties != nil && vv.ForbiddenGenerics.AdditionalProperties.Structural != nil {
|
||||
m.visitStructural(vv.ForbiddenGenerics.AdditionalProperties.Structural)
|
||||
if vv.AdditionalProperties != nil {
|
||||
m.visitNestedValueValidation(vv.AdditionalProperties)
|
||||
}
|
||||
for i := range vv.ValueValidation.AllOf {
|
||||
m.visitNestedValueValidation(&vv.ValueValidation.AllOf[i])
|
||||
|
||||
47
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/zz_generated.deepcopy.go
generated
vendored
47
vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/zz_generated.deepcopy.go
generated
vendored
@@ -43,13 +43,6 @@ func (in *Extensions) DeepCopyInto(out *Extensions) {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.XValidations != nil {
|
||||
in, out := &in.XValidations, &out.XValidations
|
||||
*out = make(v1.ValidationRules, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,11 +60,6 @@ func (in *Extensions) DeepCopy() *Extensions {
|
||||
func (in *Generic) DeepCopyInto(out *Generic) {
|
||||
*out = *in
|
||||
out.Default = in.Default.DeepCopy()
|
||||
if in.AdditionalProperties != nil {
|
||||
in, out := &in.AdditionalProperties, &out.AdditionalProperties
|
||||
*out = new(StructuralOrBool)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -89,6 +77,7 @@ func (in *Generic) DeepCopy() *Generic {
|
||||
func (in *NestedValueValidation) DeepCopyInto(out *NestedValueValidation) {
|
||||
*out = *in
|
||||
in.ValueValidation.DeepCopyInto(&out.ValueValidation)
|
||||
in.ValidationExtensions.DeepCopyInto(&out.ValidationExtensions)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = new(NestedValueValidation)
|
||||
@@ -101,6 +90,11 @@ func (in *NestedValueValidation) DeepCopyInto(out *NestedValueValidation) {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.AdditionalProperties != nil {
|
||||
in, out := &in.AdditionalProperties, &out.AdditionalProperties
|
||||
*out = new(NestedValueValidation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.ForbiddenGenerics.DeepCopyInto(&out.ForbiddenGenerics)
|
||||
in.ForbiddenExtensions.DeepCopyInto(&out.ForbiddenExtensions)
|
||||
return
|
||||
@@ -131,8 +125,14 @@ func (in *Structural) DeepCopyInto(out *Structural) {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.AdditionalProperties != nil {
|
||||
in, out := &in.AdditionalProperties, &out.AdditionalProperties
|
||||
*out = new(StructuralOrBool)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Generic.DeepCopyInto(&out.Generic)
|
||||
in.Extensions.DeepCopyInto(&out.Extensions)
|
||||
in.ValidationExtensions.DeepCopyInto(&out.ValidationExtensions)
|
||||
if in.ValueValidation != nil {
|
||||
in, out := &in.ValueValidation, &out.ValueValidation
|
||||
*out = new(ValueValidation)
|
||||
@@ -172,6 +172,29 @@ func (in *StructuralOrBool) DeepCopy() *StructuralOrBool {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ValidationExtensions) DeepCopyInto(out *ValidationExtensions) {
|
||||
*out = *in
|
||||
if in.XValidations != nil {
|
||||
in, out := &in.XValidations, &out.XValidations
|
||||
*out = make(v1.ValidationRules, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidationExtensions.
|
||||
func (in *ValidationExtensions) DeepCopy() *ValidationExtensions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ValidationExtensions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ValueValidation) DeepCopyInto(out *ValueValidation) {
|
||||
*out = *in
|
||||
|
||||
Reference in New Issue
Block a user