Refactor validation options
This commit is contained in:
		@@ -60,18 +60,40 @@ func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinitio
 | 
				
			|||||||
		return ret
 | 
							return ret
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := validationOptions{
 | 
				
			||||||
 | 
							allowDefaults:                            allowDefaults(requestGV),
 | 
				
			||||||
 | 
							requireRecognizedConversionReviewVersion: true,
 | 
				
			||||||
 | 
							requireImmutableNames:                    false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
 | 
						allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionSpec(&obj.Spec, field.NewPath("spec"))...)
 | 
						allErrs = append(allErrs, validateCustomResourceDefinitionSpec(&obj.Spec, opts, field.NewPath("spec"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
 | 
				
			||||||
	allErrs = append(allErrs, validateAPIApproval(obj, nil, requestGV)...)
 | 
						allErrs = append(allErrs, validateAPIApproval(obj, nil, requestGV)...)
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validationOptions groups several validation options, to avoid passing multiple bool parameters to methods
 | 
				
			||||||
 | 
					type validationOptions struct {
 | 
				
			||||||
 | 
						// allowDefaults permits the validation schema to contain default attributes
 | 
				
			||||||
 | 
						allowDefaults bool
 | 
				
			||||||
 | 
						// requireRecognizedConversionReviewVersion requires accepted webhook conversion versions to contain a recognized version
 | 
				
			||||||
 | 
						requireRecognizedConversionReviewVersion bool
 | 
				
			||||||
 | 
						// requireImmutableNames disables changing spec.names
 | 
				
			||||||
 | 
						requireImmutableNames bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCustomResourceDefinitionUpdate statically validates
 | 
					// ValidateCustomResourceDefinitionUpdate statically validates
 | 
				
			||||||
func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList {
 | 
					func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList {
 | 
				
			||||||
 | 
						opts := validationOptions{
 | 
				
			||||||
 | 
							allowDefaults:                            allowDefaults(requestGV) || specHasDefaults(&oldObj.Spec),
 | 
				
			||||||
 | 
							requireRecognizedConversionReviewVersion: oldObj.Spec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldObj.Spec.Conversion.ConversionReviewVersions),
 | 
				
			||||||
 | 
							requireImmutableNames:                    apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	allErrs := genericvalidation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))
 | 
						allErrs := genericvalidation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established), field.NewPath("spec"))...)
 | 
						allErrs = append(allErrs, validateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, opts, field.NewPath("spec"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
 | 
				
			||||||
	allErrs = append(allErrs, validateAPIApproval(obj, oldObj, requestGV)...)
 | 
						allErrs = append(allErrs, validateAPIApproval(obj, oldObj, requestGV)...)
 | 
				
			||||||
@@ -112,10 +134,10 @@ func ValidateUpdateCustomResourceDefinitionStatus(obj, oldObj *apiextensions.Cus
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCustomResourceDefinitionVersion statically validates.
 | 
					// validateCustomResourceDefinitionVersion statically validates.
 | 
				
			||||||
func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path, mustBeStructural, statusEnabled, allowDefaults bool) field.ErrorList {
 | 
					func validateCustomResourceDefinitionVersion(version *apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path, mustBeStructural, statusEnabled bool, opts validationOptions) field.ErrorList {
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(version.Schema, mustBeStructural, statusEnabled, allowDefaults, fldPath.Child("schema"))...)
 | 
						allErrs = append(allErrs, validateCustomResourceDefinitionValidation(version.Schema, mustBeStructural, statusEnabled, opts, fldPath.Child("schema"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(version.Subresources, fldPath.Child("subresources"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(version.Subresources, fldPath.Child("subresources"))...)
 | 
				
			||||||
	for i := range version.AdditionalPrinterColumns {
 | 
						for i := range version.AdditionalPrinterColumns {
 | 
				
			||||||
		allErrs = append(allErrs, ValidateCustomResourceColumnDefinition(&version.AdditionalPrinterColumns[i], fldPath.Child("additionalPrinterColumns").Index(i))...)
 | 
							allErrs = append(allErrs, ValidateCustomResourceColumnDefinition(&version.AdditionalPrinterColumns[i], fldPath.Child("additionalPrinterColumns").Index(i))...)
 | 
				
			||||||
@@ -123,13 +145,7 @@ func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResour
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCustomResourceDefinitionSpec statically validates
 | 
					func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, opts validationOptions, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
 | 
					 | 
				
			||||||
	allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting)
 | 
					 | 
				
			||||||
	return validateCustomResourceDefinitionSpec(spec, true, allowDefaults, fldPath)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, requireRecognizedVersion, allowDefaults bool, fldPath *field.Path) field.ErrorList {
 | 
					 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(spec.Group) == 0 {
 | 
						if len(spec.Group) == 0 {
 | 
				
			||||||
@@ -155,7 +171,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if allowDefaults && specHasDefaults(spec) {
 | 
						if opts.allowDefaults && specHasDefaults(spec) {
 | 
				
			||||||
		mustBeStructural = true
 | 
							mustBeStructural = true
 | 
				
			||||||
		if spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields == true {
 | 
							if spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields == true {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("preserveUnknownFields"), true, "must be false in order to use defaults in the schema"))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Child("preserveUnknownFields"), true, "must be false in order to use defaults in the schema"))
 | 
				
			||||||
@@ -181,7 +197,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
 | 
				
			|||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		subresources := getSubresourcesForVersion(spec, version.Name)
 | 
							subresources := getSubresourcesForVersion(spec, version.Name)
 | 
				
			||||||
		allErrs = append(allErrs, ValidateCustomResourceDefinitionVersion(&version, fldPath.Child("versions").Index(i), mustBeStructural, hasStatusEnabled(subresources), allowDefaults)...)
 | 
							allErrs = append(allErrs, validateCustomResourceDefinitionVersion(&version, fldPath.Child("versions").Index(i), mustBeStructural, hasStatusEnabled(subresources), opts)...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The top-level and per-version fields are mutual exclusive
 | 
						// The top-level and per-version fields are mutual exclusive
 | 
				
			||||||
@@ -236,7 +252,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, mustBeStructural, hasAnyStatusEnabled(spec), allowDefaults, fldPath.Child("validation"))...)
 | 
						allErrs = append(allErrs, validateCustomResourceDefinitionValidation(spec.Validation, mustBeStructural, hasAnyStatusEnabled(spec), opts, fldPath.Child("validation"))...)
 | 
				
			||||||
	allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(spec.Subresources, fldPath.Child("subresources"))...)
 | 
						allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(spec.Subresources, fldPath.Child("subresources"))...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range spec.AdditionalPrinterColumns {
 | 
						for i := range spec.AdditionalPrinterColumns {
 | 
				
			||||||
@@ -248,7 +264,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
 | 
				
			|||||||
	if (spec.Conversion != nil && spec.Conversion.Strategy != apiextensions.NoneConverter) && (spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields) {
 | 
						if (spec.Conversion != nil && spec.Conversion.Strategy != apiextensions.NoneConverter) && (spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields) {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("conversion").Child("strategy"), spec.Conversion.Strategy, "must be None if spec.preserveUnknownFields is true"))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("conversion").Child("strategy"), spec.Conversion.Strategy, "must be None if spec.preserveUnknownFields is true"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, requireRecognizedVersion, fldPath.Child("conversion"))...)
 | 
						allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, opts.requireRecognizedConversionReviewVersion, fldPath.Child("conversion"))...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -363,16 +379,11 @@ func validateCustomResourceConversion(conversion *apiextensions.CustomResourceCo
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCustomResourceDefinitionSpecUpdate statically validates
 | 
					// validateCustomResourceDefinitionSpecUpdate statically validates
 | 
				
			||||||
func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.CustomResourceDefinitionSpec, established bool, fldPath *field.Path) field.ErrorList {
 | 
					func validateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.CustomResourceDefinitionSpec, opts validationOptions, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	requireRecognizedVersion := oldSpec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldSpec.Conversion.ConversionReviewVersions)
 | 
						allErrs := validateCustomResourceDefinitionSpec(spec, opts, fldPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// find out whether any schema had default before. Then we keep allowing it.
 | 
						if opts.requireImmutableNames {
 | 
				
			||||||
	allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting) || specHasDefaults(oldSpec)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	allErrs := validateCustomResourceDefinitionSpec(spec, requireRecognizedVersion, allowDefaults, fldPath)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if established {
 | 
					 | 
				
			||||||
		// these effect the storage and cannot be changed therefore
 | 
							// these effect the storage and cannot be changed therefore
 | 
				
			||||||
		allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Scope, oldSpec.Scope, fldPath.Child("scope"))...)
 | 
							allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Scope, oldSpec.Scope, fldPath.Child("scope"))...)
 | 
				
			||||||
		allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Names.Kind, oldSpec.Names.Kind, fldPath.Child("names", "kind"))...)
 | 
							allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Names.Kind, oldSpec.Names.Kind, fldPath.Child("names", "kind"))...)
 | 
				
			||||||
@@ -577,8 +588,8 @@ type specStandardValidator interface {
 | 
				
			|||||||
	withInsideResourceMeta() specStandardValidator
 | 
						withInsideResourceMeta() specStandardValidator
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCustomResourceDefinitionValidation statically validates
 | 
					// validateCustomResourceDefinitionValidation statically validates
 | 
				
			||||||
func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiextensions.CustomResourceValidation, mustBeStructural, statusSubresourceEnabled, allowDefaults bool, fldPath *field.Path) field.ErrorList {
 | 
					func validateCustomResourceDefinitionValidation(customResourceValidation *apiextensions.CustomResourceValidation, mustBeStructural, statusSubresourceEnabled bool, opts validationOptions, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if customResourceValidation == nil {
 | 
						if customResourceValidation == nil {
 | 
				
			||||||
@@ -619,7 +630,7 @@ func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiext
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		openAPIV3Schema := &specStandardValidatorV3{
 | 
							openAPIV3Schema := &specStandardValidatorV3{
 | 
				
			||||||
			allowDefaults: allowDefaults,
 | 
								allowDefaults: opts.allowDefaults,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema, fldPath.Child("openAPIV3Schema"), openAPIV3Schema, true)...)
 | 
							allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema, fldPath.Child("openAPIV3Schema"), openAPIV3Schema, true)...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -933,6 +944,14 @@ func allowedAtRootSchema(field string) bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// allowDefaults returns true if the defaulting feature is enabled and the request group version allows adding defaults
 | 
				
			||||||
 | 
					func allowDefaults(requestGV schema.GroupVersion) bool {
 | 
				
			||||||
 | 
						if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting) {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func specHasDefaults(spec *apiextensions.CustomResourceDefinitionSpec) bool {
 | 
					func specHasDefaults(spec *apiextensions.CustomResourceDefinitionSpec) bool {
 | 
				
			||||||
	if spec.Validation != nil && schemaHasDefaults(spec.Validation.OpenAPIV3Schema) {
 | 
						if spec.Validation != nil && schemaHasDefaults(spec.Validation.OpenAPIV3Schema) {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3585,6 +3585,7 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
		input            apiextensions.CustomResourceValidation
 | 
							input            apiextensions.CustomResourceValidation
 | 
				
			||||||
		mustBeStructural bool
 | 
							mustBeStructural bool
 | 
				
			||||||
		statusEnabled    bool
 | 
							statusEnabled    bool
 | 
				
			||||||
 | 
							opts             validationOptions
 | 
				
			||||||
		wantError        bool
 | 
							wantError        bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -3726,7 +3727,7 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		t.Run(tt.name, func(t *testing.T) {
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
			got := ValidateCustomResourceDefinitionValidation(&tt.input, tt.mustBeStructural, tt.statusEnabled, false, field.NewPath("spec", "validation"))
 | 
								got := validateCustomResourceDefinitionValidation(&tt.input, tt.mustBeStructural, tt.statusEnabled, tt.opts, field.NewPath("spec", "validation"))
 | 
				
			||||||
			if !tt.wantError && len(got) > 0 {
 | 
								if !tt.wantError && len(got) > 0 {
 | 
				
			||||||
				t.Errorf("Expected no error, but got: %v", got)
 | 
									t.Errorf("Expected no error, but got: %v", got)
 | 
				
			||||||
			} else if tt.wantError && len(got) == 0 {
 | 
								} else if tt.wantError && len(got) == 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user