diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go index d7edbc121ae..25d0d45dcd8 100644 --- a/pkg/apis/networking/validation/validation.go +++ b/pkg/apis/networking/validation/validation.go @@ -27,9 +27,11 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" api "k8s.io/kubernetes/pkg/apis/core" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/networking" + "k8s.io/kubernetes/pkg/features" netutils "k8s.io/utils/net" "k8s.io/utils/ptr" ) @@ -283,6 +285,9 @@ type IngressValidationOptions struct { // AllowInvalidWildcardHostRule indicates whether invalid rule values are allowed in rules with wildcard hostnames AllowInvalidWildcardHostRule bool + + // AllowRelaxedServiceNameValidation indicates if the backend service name can be validated with apimachineryvalidation.NameIsDNSLabel + AllowRelaxedServiceNameValidation bool } // ValidateIngress validates Ingresses on create and update. @@ -296,8 +301,9 @@ func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions) func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList { allErrs := field.ErrorList{} opts := IngressValidationOptions{ - AllowInvalidSecretName: false, - AllowInvalidWildcardHostRule: false, + AllowInvalidSecretName: false, + AllowInvalidWildcardHostRule: false, + AllowRelaxedServiceNameValidation: allowRelaxedServiceNameValidation(nil), } allErrs = append(allErrs, validateIngress(ingress, opts)...) annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass] @@ -312,8 +318,9 @@ func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList { func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList { allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) opts := IngressValidationOptions{ - AllowInvalidSecretName: allowInvalidSecretName(oldIngress), - AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress), + AllowInvalidSecretName: allowInvalidSecretName(oldIngress), + AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress), + AllowRelaxedServiceNameValidation: allowRelaxedServiceNameValidation(oldIngress), } allErrs = append(allErrs, validateIngress(ingress, opts)...) @@ -513,7 +520,13 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P if len(backend.Service.Name) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), "")) } else { - for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) { + + validationFunc := apivalidation.ValidateServiceName + if opts.AllowRelaxedServiceNameValidation { + validationFunc = apimachineryvalidation.NameIsDNSLabel + } + + for _, msg := range validationFunc(backend.Service.Name, false) { allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg)) } } @@ -685,6 +698,37 @@ func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool { return false } +func allowRelaxedServiceNameValidation(oldIngress *networking.Ingress) bool { + // Early exit if the feature gate is enabled, as it allows relaxed validation + if utilfeature.DefaultFeatureGate.Enabled(features.RelaxedServiceNameValidation) { + return true + } + // Early exit if no old Ingress is provided + if oldIngress == nil { + return false + } + // If feature gate is disabled, check if any service names in the old Ingresss + for _, rule := range oldIngress.Spec.Rules { + if rule.HTTP == nil { + continue + } + for _, path := range rule.HTTP.Paths { + if path.Backend.Service == nil { + continue + } + serviceName := path.Backend.Service.Name + // If a name doesn't validate with apimachineryvalidation.NameIsDNS1035Label, but does validate with apimachineryvalidation.NameIsDNSLabel, + // then we allow it to be used as a Service name in an Ingress. + dnsLabelValidationErrors := apimachineryvalidation.NameIsDNSLabel(serviceName, false) + dns1035LabelValidationErrors := apimachineryvalidation.NameIsDNS1035Label(serviceName, false) + if len(dnsLabelValidationErrors) == 0 && len(dns1035LabelValidationErrors) > 0 { + return true + } + } + } + return false +} + // ValidateIPAddressName validates that the name is the decimal representation of an IP address. // IPAddress does not support generating names, prefix is not considered. func ValidateIPAddressName(name string, prefix bool) []string {