Drop legacy validation logic for networking API

This commit is contained in:
Jordan Liggitt 2021-08-09 12:37:45 -04:00
parent b1d344db44
commit c702dd4394
3 changed files with 51 additions and 274 deletions

View File

@ -21,12 +21,9 @@ import (
"net" "net"
"strings" "strings"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path" pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
@ -230,20 +227,20 @@ type IngressValidationOptions struct {
} }
// ValidateIngress validates Ingresses on create and update. // ValidateIngress validates Ingresses on create and update.
func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts, requestGV)...) allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts)...)
return allErrs return allErrs
} }
// ValidateIngressCreate validates Ingresses on create. // ValidateIngressCreate validates Ingresses on create.
func ValidateIngressCreate(ingress *networking.Ingress, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
opts := IngressValidationOptions{ opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, nil), AllowInvalidSecretName: false,
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, nil), AllowInvalidWildcardHostRule: false,
} }
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...) allErrs = append(allErrs, validateIngress(ingress, opts)...)
annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass] annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
if annotationIsSet && ingress.Spec.IngressClassName != nil { if annotationIsSet && ingress.Spec.IngressClassName != nil {
annotationPath := field.NewPath("annotations").Child(annotationIngressClass) annotationPath := field.NewPath("annotations").Child(annotationIngressClass)
@ -253,14 +250,14 @@ func ValidateIngressCreate(ingress *networking.Ingress, requestGV schema.GroupVe
} }
// ValidateIngressUpdate validates ingresses on update. // ValidateIngressUpdate validates ingresses on update.
func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
opts := IngressValidationOptions{ opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, oldIngress), AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, oldIngress), AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
} }
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...) allErrs = append(allErrs, validateIngress(ingress, opts)...)
return allErrs return allErrs
} }
@ -291,29 +288,18 @@ func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path, opts
return allErrs return allErrs
} }
// defaultBackendFieldName returns the name of the field used for defaultBackend
// in the provided GroupVersion.
func defaultBackendFieldName(gv schema.GroupVersion) string {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return "backend"
default:
return "defaultBackend"
}
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set. // ValidateIngressSpec tests if required fields in the IngressSpec are set.
func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(spec.Rules) == 0 && spec.DefaultBackend == nil { if len(spec.Rules) == 0 && spec.DefaultBackend == nil {
errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", defaultBackendFieldName(requestGV)) errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", "defaultBackend")
allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg)) allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg))
} }
if spec.DefaultBackend != nil { if spec.DefaultBackend != nil {
allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child(defaultBackendFieldName(requestGV)), opts, requestGV)...) allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child("defaultBackend"), opts)...)
} }
if len(spec.Rules) > 0 { if len(spec.Rules) > 0 {
allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts, requestGV)...) allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts)...)
} }
if len(spec.TLS) > 0 { if len(spec.TLS) > 0 {
allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...) allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...)
@ -333,7 +319,7 @@ func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.
return allErrs return allErrs
} }
func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(ingressRules) == 0 { if len(ingressRules) == 0 {
return append(allErrs, field.Required(fldPath, "")) return append(allErrs, field.Required(fldPath, ""))
@ -359,32 +345,32 @@ func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.
} }
if !wildcardHost || !opts.AllowInvalidWildcardHostRule { if !wildcardHost || !opts.AllowInvalidWildcardHostRule {
allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts, requestGV)...) allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts)...)
} }
} }
return allErrs return allErrs
} }
func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if ingressRule.HTTP != nil { if ingressRule.HTTP != nil {
allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts, requestGV)...) allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts)...)
} }
return allErrs return allErrs
} }
func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(httpIngressRuleValue.Paths) == 0 { if len(httpIngressRuleValue.Paths) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("paths"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
} }
for i, path := range httpIngressRuleValue.Paths { for i, path := range httpIngressRuleValue.Paths {
allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts, requestGV)...) allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts)...)
} }
return allErrs return allErrs
} }
func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if path.PathType == nil { if path.PathType == nil {
@ -418,45 +404,12 @@ func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Pa
default: default:
allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List()))
} }
allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts, requestGV)...) allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts)...)
return allErrs return allErrs
} }
// numberPortField returns the field path to a service port number field
// relative to a backend struct in the provided GroupVersion
func numberPortField(numberPortFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return numberPortFieldPath.Child("servicePort")
default:
return numberPortFieldPath.Child("service", "port", "number")
}
}
// namedPortField returns the field path to a service port name field
// relative to a backend struct in the provided GroupVersion
func namedPortField(namedPortFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return namedPortFieldPath.Child("servicePort")
default:
return namedPortFieldPath.Child("service", "port", "name")
}
}
// serviceNameFieldPath returns the name of the field used for a
// service name in the provided GroupVersion.
func serviceNameFieldPath(backendFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return backendFieldPath.Child("serviceName")
default:
return backendFieldPath.Child("service", "name")
}
}
// validateIngressBackend tests if a given backend is valid. // validateIngressBackend tests if a given backend is valid.
func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
hasResourceBackend := backend.Resource != nil hasResourceBackend := backend.Resource != nil
@ -470,10 +423,10 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
case hasServiceBackend: case hasServiceBackend:
if len(backend.Service.Name) == 0 { if len(backend.Service.Name) == 0 {
allErrs = append(allErrs, field.Required(serviceNameFieldPath(fldPath, requestGV), "")) allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
} else { } else {
for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) { for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) {
allErrs = append(allErrs, field.Invalid(serviceNameFieldPath(fldPath, requestGV), backend.Service.Name, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg))
} }
} }
@ -483,11 +436,11 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number")) allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number"))
} else if hasPortName { } else if hasPortName {
for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) { for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) {
allErrs = append(allErrs, field.Invalid(namedPortField(fldPath, requestGV), backend.Service.Port.Name, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "name"), backend.Service.Port.Name, msg))
} }
} else if hasPortNumber { } else if hasPortNumber {
for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) { for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) {
allErrs = append(allErrs, field.Invalid(numberPortField(fldPath, requestGV), backend.Service.Port.Number, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "number"), backend.Service.Port.Number, msg))
} }
} else { } else {
allErrs = append(allErrs, field.Required(fldPath, "port name or number is required")) allErrs = append(allErrs, field.Required(fldPath, "port name or number is required"))
@ -616,11 +569,7 @@ func validateIngressClassParametersReference(params *networking.IngressClassPara
return allErrs return allErrs
} }
func allowInvalidSecretName(gv schema.GroupVersion, oldIngress *networking.Ingress) bool { func allowInvalidSecretName(oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid names
return true
}
if oldIngress != nil { if oldIngress != nil {
for _, tls := range oldIngress.Spec.TLS { for _, tls := range oldIngress.Spec.TLS {
if len(validateTLSSecretName(tls.SecretName)) > 0 { if len(validateTLSSecretName(tls.SecretName)) > 0 {
@ -639,14 +588,10 @@ func validateTLSSecretName(name string) []string {
return apivalidation.ValidateSecretName(name, false) return apivalidation.ValidateSecretName(name, false)
} }
func allowInvalidWildcardHostRule(gv schema.GroupVersion, oldIngress *networking.Ingress) bool { func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid rules
return true
}
if oldIngress != nil { if oldIngress != nil {
for _, rule := range oldIngress.Spec.Rules { for _, rule := range oldIngress.Spec.Rules {
if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{}, gv)) > 0 { if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{})) > 0 {
// backwards compatibility with existing invalid data // backwards compatibility with existing invalid data
return true return true
} }

View File

@ -21,11 +21,8 @@ import (
"strings" "strings"
"testing" "testing"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -514,7 +511,6 @@ func TestValidateIngress(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
tweakIngress func(ing *networking.Ingress) tweakIngress func(ing *networking.Ingress)
expectErrsOnFields []string expectErrsOnFields []string
}{ }{
@ -531,13 +527,12 @@ func TestValidateIngress(t *testing.T) {
expectErrsOnFields: []string{}, expectErrsOnFields: []string{},
}, },
// invalid use cases // invalid use cases
"backend (v1beta1) with no service": { "backend with no service": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend.Service.Name = "" ing.Spec.DefaultBackend.Service.Name = ""
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend.serviceName", "spec.defaultBackend.service.name",
}, },
}, },
"invalid path type": { "invalid path type": {
@ -654,7 +649,6 @@ func TestValidateIngress(t *testing.T) {
}, },
}, },
"spec.backend resource and service name are not allowed together": { "spec.backend resource and service name are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{ ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend, Service: serviceBackend,
@ -666,11 +660,10 @@ func TestValidateIngress(t *testing.T) {
} }
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend", "spec.defaultBackend",
}, },
}, },
"spec.backend resource and service port are not allowed together": { "spec.backend resource and service port are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{ ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend, Service: serviceBackend,
@ -682,7 +675,7 @@ func TestValidateIngress(t *testing.T) {
} }
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend", "spec.defaultBackend",
}, },
}, },
} }
@ -691,11 +684,7 @@ func TestValidateIngress(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
ingress := baseIngress.DeepCopy() ingress := baseIngress.DeepCopy()
testCase.tweakIngress(ingress) testCase.tweakIngress(ingress)
gv := testCase.groupVersion errs := validateIngress(ingress, IngressValidationOptions{})
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngress(ingress, IngressValidationOptions{}, *gv)
if len(testCase.expectErrsOnFields) != len(errs) { if len(testCase.expectErrsOnFields) != len(errs) {
t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectErrsOnFields), len(errs), errs) t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectErrsOnFields), len(errs), errs)
} }
@ -718,7 +707,6 @@ func TestValidateIngressRuleValue(t *testing.T) {
} }
fldPath := field.NewPath("testing.http.paths[0].path") fldPath := field.NewPath("testing.http.paths[0].path")
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
pathType networking.PathType pathType networking.PathType
path string path string
expectedErrs field.ErrorList expectedErrs field.ErrorList
@ -820,11 +808,7 @@ func TestValidateIngressRuleValue(t *testing.T) {
}, },
}, },
} }
gv := testCase.groupVersion errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{})
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{}, *gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
} }
@ -868,7 +852,6 @@ func TestValidateIngressCreate(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
tweakIngress func(ingress *networking.Ingress) tweakIngress func(ingress *networking.Ingress)
expectedErrs field.ErrorList expectedErrs field.ErrorList
}{ }{
@ -950,33 +933,18 @@ func TestValidateIngressCreate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{}, expectedErrs: field.ErrorList{},
}, },
"v1: valid secret": { "valid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}} ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
}, },
}, },
"v1: invalid secret": { "invalid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}} ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)},
}, },
"v1beta1: valid secret": { "valid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
},
},
"v1beta1: invalid secret": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
},
},
"v1: valid rules with wildcard host": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{ ingress.Spec.Rules = []networking.IngressRule{{
@ -993,8 +961,7 @@ func TestValidateIngressCreate(t *testing.T) {
}} }}
}, },
}, },
"v1: invalid rules with wildcard host": { "invalid rules with wildcard host": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{ ingress.Spec.Rules = []networking.IngressRule{{
@ -1012,53 +979,13 @@ func TestValidateIngressCreate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)},
}, },
"v1beta1: valid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "/foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: invalid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
} }
for name, testCase := range testCases { for name, testCase := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
newIngress := baseIngress.DeepCopy() newIngress := baseIngress.DeepCopy()
testCase.tweakIngress(newIngress) testCase.tweakIngress(newIngress)
gv := testCase.groupVersion errs := ValidateIngressCreate(newIngress)
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := ValidateIngressCreate(newIngress, *gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
} }
@ -1101,7 +1028,6 @@ func TestValidateIngressUpdate(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
gv schema.GroupVersion
tweakIngresses func(newIngress, oldIngress *networking.Ingress) tweakIngresses func(newIngress, oldIngress *networking.Ingress)
expectedErrs field.ErrorList expectedErrs field.ErrorList
}{ }{
@ -1385,37 +1311,20 @@ func TestValidateIngressUpdate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{}, expectedErrs: field.ErrorList{},
}, },
"v1: change valid secret -> invalid secret": { "change valid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}} oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}} newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)},
}, },
"v1: change invalid secret -> invalid secret": { "change invalid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}} oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}} newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}}
}, },
}, },
"v1beta1: change valid secret -> invalid secret": { "change valid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
},
},
"v1beta1: change invalid secret -> invalid secret": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}}
},
},
"v1: change valid rules with wildcard host -> invalid rules": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{ oldIngress.Spec.Rules = []networking.IngressRule{{
@ -1446,70 +1355,7 @@ func TestValidateIngressUpdate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)},
}, },
"v1: change invalid rules with wildcard host -> invalid rules": { "change invalid rules with wildcard host -> invalid rules": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
newIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
newIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "bar",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: change valid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "/foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
newIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
newIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: change invalid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{ oldIngress.Spec.Rules = []networking.IngressRule{{
@ -1547,11 +1393,7 @@ func TestValidateIngressUpdate(t *testing.T) {
oldIngress := baseIngress.DeepCopy() oldIngress := baseIngress.DeepCopy()
testCase.tweakIngresses(newIngress, oldIngress) testCase.tweakIngresses(newIngress, oldIngress)
gv := testCase.gv errs := ValidateIngressUpdate(newIngress, oldIngress)
if gv.Empty() {
gv = networkingv1beta1.SchemeGroupVersion
}
errs := ValidateIngressUpdate(newIngress, oldIngress, gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
@ -1861,7 +1703,7 @@ func TestValidateIngressTLS(t *testing.T) {
errorCases[badWildcardTLSErr] = badWildcardTLS errorCases[badWildcardTLSErr] = badWildcardTLS
for k, v := range errorCases { for k, v := range errorCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure for %q", k) t.Errorf("expected failure for %q", k)
} else { } else {
@ -1885,7 +1727,7 @@ func TestValidateIngressTLS(t *testing.T) {
} }
validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS
for k, v := range validCases { for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("expected success for %q", k) t.Errorf("expected success for %q", k)
} }
@ -1946,7 +1788,7 @@ func TestValidateEmptyIngressTLS(t *testing.T) {
} }
validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyHosts.Spec.TLS[0])] = goodEmptyHosts validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyHosts.Spec.TLS[0])] = goodEmptyHosts
for k, v := range validCases { for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("expected success for %q", k) t.Errorf("expected success for %q", k)
} }

View File

@ -21,9 +21,7 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/networking"
@ -90,12 +88,8 @@ func (ingressStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
// Validate validates ingresses on create. // Validate validates ingresses on create.
func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
var requestGV schema.GroupVersion
if requestInfo, ok := request.RequestInfoFrom(ctx); ok {
requestGV = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
ingress := obj.(*networking.Ingress) ingress := obj.(*networking.Ingress)
return validation.ValidateIngressCreate(ingress, requestGV) return validation.ValidateIngressCreate(ingress)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.
@ -112,11 +106,7 @@ func (ingressStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate validates ingresses on update. // ValidateUpdate validates ingresses on update.
func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var requestGV schema.GroupVersion return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
if requestInfo, ok := request.RequestInfoFrom(ctx); ok {
requestGV = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress), requestGV)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.