Add namespace scoped ParametersReference to IngressClass

This commit is contained in:
Harry Bagdi
2021-03-06 03:03:20 +05:30
parent a954818194
commit a7fc92089a
44 changed files with 2535 additions and 225 deletions

View File

@@ -313,7 +313,42 @@ type IngressClassSpec struct {
// configuration for the controller. This is optional if the controller does
// not require extra parameters.
// +optional
Parameters *api.TypedLocalObjectReference
Parameters *IngressClassParametersReference
}
const (
// IngressClassParametersReferenceScopeNamespace indicates that the
// referenced Parameters resource is namespace-scoped.
IngressClassParametersReferenceScopeNamespace = "Namespace"
// IngressClassParametersReferenceScopeNamespace indicates that the
// referenced Parameters resource is cluster-scoped.
IngressClassParametersReferenceScopeCluster = "Cluster"
)
// IngressClassParametersReference identifies an API object. This can be used
// to specify a cluster or namespace-scoped resource.
type IngressClassParametersReference struct {
// APIGroup is the group for the resource being referenced. If APIGroup is
// not specified, the specified Kind must be in the core API group. For any
// other third-party types, APIGroup is required.
// +optional
APIGroup *string
// Kind is the type of resource being referenced.
Kind string
// Name is the name of resource being referenced.
Name string
// Scope represents if this refers to a cluster or namespace scoped resource.
// This may be set to "Cluster" (default) or "Namespace".
// Field can be enabled with IngressClassNamespacedParams feature gate.
// +optional
// +featureGate=IngressClassNamespacedParams
Scope *string
// Namespace is the namespace of the resource being referenced. This field is
// required when scope is set to "Namespace" and must be unset when scope is set to
// "Cluster".
// +optional
// +featureGate=IngressClassNamespacedParams
Namespace *string
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -20,6 +20,9 @@ import (
"k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
@@ -43,3 +46,12 @@ func SetDefaults_NetworkPolicy(obj *networkingv1.NetworkPolicy) {
}
}
}
func SetDefaults_IngressClass(obj *networkingv1.IngressClass) {
if !utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) {
return
}
if obj.Spec.Parameters != nil && obj.Spec.Parameters.Scope == nil {
obj.Spec.Parameters.Scope = utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeCluster)
}
}

View File

@@ -24,10 +24,14 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
_ "k8s.io/kubernetes/pkg/apis/core/install"
_ "k8s.io/kubernetes/pkg/apis/networking/install"
. "k8s.io/kubernetes/pkg/apis/networking/v1"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
func TestSetDefaultNetworkPolicy(t *testing.T) {
@@ -234,6 +238,132 @@ func TestSetDefaultNetworkPolicy(t *testing.T) {
}
}
func TestSetDefaultsForIngressClassParametersReference(t *testing.T) {
tests := []struct {
name string
original *networkingv1.IngressClass
expected *networkingv1.IngressClass
enableNamespaceScopedParamsGate bool
}{
{
name: "populated parameters sets the default Scope when feature is enabled",
original: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
},
},
},
expected: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeCluster),
},
},
},
enableNamespaceScopedParamsGate: true,
},
{
name: "existing scope is not overridden when feature is enabled",
original: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeNamespace),
Namespace: utilpointer.StringPtr("foo-ns"),
},
},
},
expected: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeNamespace),
Namespace: utilpointer.StringPtr("foo-ns"),
},
},
},
enableNamespaceScopedParamsGate: true,
},
{
name: "empty Parameters does not set the default Scope when feature is enabled",
original: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
},
},
expected: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
},
},
enableNamespaceScopedParamsGate: true,
},
{
name: "populated parameters does not set the default Scope when feature is disabled",
original: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
},
},
},
expected: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
Parameters: &networkingv1.IngressClassParametersReference{
Kind: "k",
Name: "n",
},
},
},
enableNamespaceScopedParamsGate: false,
},
{
name: "empty Parameters does not set the default Scope when feature is disabled",
original: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
},
},
expected: &networkingv1.IngressClass{
Spec: networkingv1.IngressClassSpec{
Controller: "controller",
},
},
enableNamespaceScopedParamsGate: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
original := test.original
expected := test.expected
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IngressClassNamespacedParams, test.enableNamespaceScopedParamsGate)()
obj2 := roundTrip(t, runtime.Object(original))
got, ok := obj2.(*networkingv1.IngressClass)
if !ok {
t.Errorf("unexpected object: %v", got)
t.FailNow()
}
if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", got.Spec, expected.Spec)
}
})
}
}
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
t.Helper()
data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj)

View File

@@ -111,6 +111,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.IngressClassParametersReference)(nil), (*networking.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(a.(*v1.IngressClassParametersReference), b.(*networking.IngressClassParametersReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.IngressClassParametersReference)(nil), (*v1.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(a.(*networking.IngressClassParametersReference), b.(*v1.IngressClassParametersReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.IngressClassSpec)(nil), (*networking.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_IngressClassSpec_To_networking_IngressClassSpec(a.(*v1.IngressClassSpec), b.(*networking.IngressClassSpec), scope)
}); err != nil {
@@ -446,9 +456,37 @@ func Convert_networking_IngressClassList_To_v1_IngressClassList(in *networking.I
return autoConvert_networking_IngressClassList_To_v1_IngressClassList(in, out, s)
}
func autoConvert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Scope = (*string)(unsafe.Pointer(in.Scope))
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference is an autogenerated conversion function.
func Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
return autoConvert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in, out, s)
}
func autoConvert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1.IngressClassParametersReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Scope = (*string)(unsafe.Pointer(in.Scope))
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference is an autogenerated conversion function.
func Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1.IngressClassParametersReference, s conversion.Scope) error {
return autoConvert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in, out, s)
}
func autoConvert_v1_IngressClassSpec_To_networking_IngressClassSpec(in *v1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error {
out.Controller = in.Controller
out.Parameters = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
out.Parameters = (*networking.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
return nil
}
@@ -459,7 +497,7 @@ func Convert_v1_IngressClassSpec_To_networking_IngressClassSpec(in *v1.IngressCl
func autoConvert_networking_IngressClassSpec_To_v1_IngressClassSpec(in *networking.IngressClassSpec, out *v1.IngressClassSpec, s conversion.Scope) error {
out.Controller = in.Controller
out.Parameters = (*corev1.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
out.Parameters = (*v1.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
return nil
}

View File

@@ -29,11 +29,24 @@ import (
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&v1.IngressClass{}, func(obj interface{}) { SetObjectDefaults_IngressClass(obj.(*v1.IngressClass)) })
scheme.AddTypeDefaultingFunc(&v1.IngressClassList{}, func(obj interface{}) { SetObjectDefaults_IngressClassList(obj.(*v1.IngressClassList)) })
scheme.AddTypeDefaultingFunc(&v1.NetworkPolicy{}, func(obj interface{}) { SetObjectDefaults_NetworkPolicy(obj.(*v1.NetworkPolicy)) })
scheme.AddTypeDefaultingFunc(&v1.NetworkPolicyList{}, func(obj interface{}) { SetObjectDefaults_NetworkPolicyList(obj.(*v1.NetworkPolicyList)) })
return nil
}
func SetObjectDefaults_IngressClass(in *v1.IngressClass) {
SetDefaults_IngressClass(in)
}
func SetObjectDefaults_IngressClassList(in *v1.IngressClassList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_IngressClass(a)
}
}
func SetObjectDefaults_NetworkPolicy(in *v1.NetworkPolicy) {
SetDefaults_NetworkPolicy(in)
for i := range in.Spec.Ingress {

View File

@@ -89,6 +89,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassParametersReference)(nil), (*networking.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(a.(*v1beta1.IngressClassParametersReference), b.(*networking.IngressClassParametersReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.IngressClassParametersReference)(nil), (*v1beta1.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(a.(*networking.IngressClassParametersReference), b.(*v1beta1.IngressClassParametersReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassSpec)(nil), (*networking.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(a.(*v1beta1.IngressClassSpec), b.(*networking.IngressClassSpec), scope)
}); err != nil {
@@ -333,9 +343,37 @@ func Convert_networking_IngressClassList_To_v1beta1_IngressClassList(in *network
return autoConvert_networking_IngressClassList_To_v1beta1_IngressClassList(in, out, s)
}
func autoConvert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1beta1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Scope = (*string)(unsafe.Pointer(in.Scope))
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference is an autogenerated conversion function.
func Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1beta1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
return autoConvert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in, out, s)
}
func autoConvert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1beta1.IngressClassParametersReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Scope = (*string)(unsafe.Pointer(in.Scope))
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference is an autogenerated conversion function.
func Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1beta1.IngressClassParametersReference, s conversion.Scope) error {
return autoConvert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in, out, s)
}
func autoConvert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error {
out.Controller = in.Controller
out.Parameters = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
out.Parameters = (*networking.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
return nil
}
@@ -346,7 +384,7 @@ func Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1
func autoConvert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(in *networking.IngressClassSpec, out *v1beta1.IngressClassSpec, s conversion.Scope) error {
out.Controller = in.Controller
out.Parameters = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
out.Parameters = (*v1beta1.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
return nil
}

View File

@@ -31,9 +31,12 @@ 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"
utilpointer "k8s.io/utils/pointer"
)
const (
@@ -49,6 +52,11 @@ var (
)
invalidPathSequences = []string{"//", "/./", "/../", "%2f", "%2F"}
invalidPathSuffixes = []string{"/..", "/."}
supportedIngressClassParametersReferenceScopes = sets.NewString(
networking.IngressClassParametersReferenceScopeNamespace,
networking.IngressClassParametersReferenceScopeCluster,
)
)
// ValidateNetworkPolicyName can be used to check whether the given networkpolicy
@@ -518,7 +526,7 @@ func validateIngressClassSpec(spec *networking.IngressClassSpec, fldPath *field.
allErrs = append(allErrs, field.TooLong(fldPath.Child("controller"), spec.Controller, maxLenIngressClassController))
}
allErrs = append(allErrs, validation.IsDomainPrefixedPath(fldPath.Child("controller"), spec.Controller)...)
allErrs = append(allErrs, validateIngressTypedLocalObjectReference(spec.Parameters, fldPath.Child("parameters"))...)
allErrs = append(allErrs, validateIngressClassParametersReference(spec.Parameters, fldPath.Child("parameters"))...)
return allErrs
}
@@ -561,6 +569,55 @@ func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectRefere
return allErrs
}
// validateIngressClassParametersReference ensures that Parameters fields are valid.
// Parameters was previously of type `TypedLocalObjectReference` and used
// `validateIngressTypedLocalObjectReference()`. This function extends validation
// for additional fields introduced for namespace-scoped references.
func validateIngressClassParametersReference(params *networking.IngressClassParametersReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if params == nil {
return allErrs
}
allErrs = append(allErrs, validateIngressTypedLocalObjectReference(&api.TypedLocalObjectReference{
APIGroup: params.APIGroup,
Kind: params.Kind,
Name: params.Name,
}, fldPath)...)
if utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) && params.Scope == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
return allErrs
}
if params.Scope != nil || params.Namespace != nil {
scope := utilpointer.StringPtrDerefOr(params.Scope, "")
if !supportedIngressClassParametersReferenceScopes.Has(scope) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), scope,
supportedIngressClassParametersReferenceScopes.List()))
} else {
if scope == networking.IngressClassParametersReferenceScopeNamespace {
if params.Namespace == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "`parameters.scope` is set to 'Namespace'"))
} else {
for _, msg := range apivalidation.ValidateNamespaceName(*params.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *params.Namespace, msg))
}
}
}
if scope == networking.IngressClassParametersReferenceScopeCluster && params.Namespace != nil {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "`parameters.scope` is set to 'Cluster'"))
}
}
}
return allErrs
}
func allowInvalidSecretName(gv schema.GroupVersion, oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid names

View File

@@ -28,8 +28,11 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
)
@@ -2239,8 +2242,9 @@ func TestValidateIngressUpdate(t *testing.T) {
func TestValidateIngressClass(t *testing.T) {
testCases := map[string]struct {
ingressClass networking.IngressClass
expectedErrs field.ErrorList
ingressClass networking.IngressClass
expectedErrs field.ErrorList
enableNamespaceScopedParamsGate bool
}{
"valid name, valid controller": {
ingressClass: networking.IngressClass{
@@ -2292,7 +2296,7 @@ func TestValidateIngressClass(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &api.TypedLocalObjectReference{
Parameters: &networking.IngressClassParametersReference{
APIGroup: utilpointer.StringPtr("example.com"),
Kind: "foo",
Name: "bar",
@@ -2306,7 +2310,7 @@ func TestValidateIngressClass(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &api.TypedLocalObjectReference{
Parameters: &networking.IngressClassParametersReference{
APIGroup: utilpointer.StringPtr("example.com"),
Name: "bar",
},
@@ -2319,7 +2323,7 @@ func TestValidateIngressClass(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &api.TypedLocalObjectReference{
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
},
},
@@ -2331,7 +2335,7 @@ func TestValidateIngressClass(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &api.TypedLocalObjectReference{
Parameters: &networking.IngressClassParametersReference{
Kind: "foo/",
Name: "bar",
},
@@ -2339,10 +2343,173 @@ func TestValidateIngressClass(t *testing.T) {
},
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.kind"), "foo/", "may not contain '/'")},
},
"valid name, valid controller, invalid params (bad scope)": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("bad-scope"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
"bad-scope", []string{"Cluster", "Namespace"})},
},
"valid name, valid controller, valid Namespace scope": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Namespace"),
Namespace: utilpointer.StringPtr("foo-ns"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{},
},
"valid name, valid controller, valid scope, invalid namespace": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Namespace"),
Namespace: utilpointer.StringPtr("foo_ns"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.namespace"), "foo_ns",
"a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-',"+
" and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', "+
"regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')")},
},
"valid name, valid controller, valid Cluster scope": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Cluster"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{},
},
"namespace not set when scope is Namespace": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Namespace"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.namespace"),
"`parameters.scope` is set to 'Namespace'")},
},
"namespace is forbidden when scope is Cluster": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Cluster"),
Namespace: utilpointer.StringPtr("foo-ns"),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.Forbidden(field.NewPath("spec.parameters.namespace"),
"`parameters.scope` is set to 'Cluster'")},
},
"empty namespace is forbidden when scope is Cluster": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("Cluster"),
Namespace: utilpointer.StringPtr(""),
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.Forbidden(field.NewPath("spec.parameters.namespace"),
"`parameters.scope` is set to 'Cluster'")},
},
"validation is performed when feature gate is disabled and scope is not empty": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Scope: utilpointer.StringPtr("bad-scope"),
},
},
},
enableNamespaceScopedParamsGate: false,
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
"bad-scope", []string{"Cluster", "Namespace"})},
},
"validation fails when feature gate is enabled and scope is not set": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
},
},
},
enableNamespaceScopedParamsGate: true,
expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.scope"), "")},
},
"validation is performed when feature gate is disabled and namespace is not empty": {
ingressClass: networking.IngressClass{
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &networking.IngressClassParametersReference{
Kind: "foo",
Name: "bar",
Namespace: utilpointer.StringPtr("foo-ns"),
},
},
},
enableNamespaceScopedParamsGate: false,
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
"", []string{"Cluster", "Namespace"})},
},
}
for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IngressClassNamespacedParams, testCase.enableNamespaceScopedParamsGate)()
errs := ValidateIngressClass(&testCase.ingressClass)
if len(errs) != len(testCase.expectedErrs) {
@@ -2390,7 +2557,7 @@ func TestValidateIngressClassUpdate(t *testing.T) {
},
Spec: networking.IngressClassSpec{
Controller: "foo.co/bar",
Parameters: &api.TypedLocalObjectReference{
Parameters: &networking.IngressClassParametersReference{
APIGroup: utilpointer.StringPtr("v1"),
Kind: "ConfigMap",
Name: "foo",

View File

@@ -207,12 +207,43 @@ func (in *IngressClassList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressClassParametersReference) DeepCopyInto(out *IngressClassParametersReference) {
*out = *in
if in.APIGroup != nil {
in, out := &in.APIGroup, &out.APIGroup
*out = new(string)
**out = **in
}
if in.Scope != nil {
in, out := &in.Scope, &out.Scope
*out = new(string)
**out = **in
}
if in.Namespace != nil {
in, out := &in.Namespace, &out.Namespace
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassParametersReference.
func (in *IngressClassParametersReference) DeepCopy() *IngressClassParametersReference {
if in == nil {
return nil
}
out := new(IngressClassParametersReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressClassSpec) DeepCopyInto(out *IngressClassSpec) {
*out = *in
if in.Parameters != nil {
in, out := &in.Parameters, &out.Parameters
*out = new(core.TypedLocalObjectReference)
*out = new(IngressClassParametersReference)
(*in).DeepCopyInto(*out)
}
return