mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-01 02:38:12 +00:00 
			
		
		
		
	Merge pull request #104636 from hbagdi/ingress-class-namespaced-params-ga
Graduate IngressClassNamespacedParams to GA
This commit is contained in:
		
							
								
								
									
										2
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							| @@ -12539,7 +12539,7 @@ | ||||
|           "type": "string" | ||||
|         }, | ||||
|         "scope": { | ||||
|           "description": "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.", | ||||
|           "description": "Scope represents if this refers to a cluster or namespace scoped resource. This may be set to \"Cluster\" (default) or \"Namespace\".", | ||||
|           "type": "string" | ||||
|         } | ||||
|       }, | ||||
|   | ||||
| @@ -339,15 +339,12 @@ type IngressClassParametersReference struct { | ||||
| 	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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -20,8 +20,6 @@ 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" | ||||
| ) | ||||
|  | ||||
| @@ -48,9 +46,6 @@ 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) | ||||
| 	} | ||||
|   | ||||
| @@ -24,13 +24,10 @@ 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" | ||||
| ) | ||||
|  | ||||
| @@ -240,13 +237,12 @@ func TestSetDefaultNetworkPolicy(t *testing.T) { | ||||
|  | ||||
| func TestSetDefaultsForIngressClassParametersReference(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                            string | ||||
| 		original                        *networkingv1.IngressClass | ||||
| 		expected                        *networkingv1.IngressClass | ||||
| 		enableNamespaceScopedParamsGate bool | ||||
| 		name     string | ||||
| 		original *networkingv1.IngressClass | ||||
| 		expected *networkingv1.IngressClass | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "populated parameters sets the default Scope when feature is enabled", | ||||
| 			name: "populated parameters sets the default Scope", | ||||
| 			original: &networkingv1.IngressClass{ | ||||
| 				Spec: networkingv1.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| @@ -266,10 +262,9 @@ func TestSetDefaultsForIngressClassParametersReference(t *testing.T) { | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "existing scope is not overridden when feature is enabled", | ||||
| 			name: "existing scope is not overridden", | ||||
| 			original: &networkingv1.IngressClass{ | ||||
| 				Spec: networkingv1.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| @@ -292,10 +287,9 @@ func TestSetDefaultsForIngressClassParametersReference(t *testing.T) { | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "empty Parameters does not set the default Scope when feature is enabled", | ||||
| 			name: "empty Parameters does not set the default Scope", | ||||
| 			original: &networkingv1.IngressClass{ | ||||
| 				Spec: networkingv1.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| @@ -306,43 +300,6 @@ func TestSetDefaultsForIngressClassParametersReference(t *testing.T) { | ||||
| 					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, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -350,7 +307,6 @@ func TestSetDefaultsForIngressClassParametersReference(t *testing.T) { | ||||
| 		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 { | ||||
|   | ||||
| @@ -27,11 +27,9 @@ 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" | ||||
| 	utilpointer "k8s.io/utils/pointer" | ||||
| ) | ||||
| @@ -537,7 +535,7 @@ func validateIngressClassParametersReference(params *networking.IngressClassPara | ||||
| 		Name:     params.Name, | ||||
| 	}, fldPath)...) | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) && params.Scope == nil { | ||||
| 	if params.Scope == nil { | ||||
| 		allErrs = append(allErrs, field.Required(fldPath.Child("scope"), "")) | ||||
| 		return allErrs | ||||
| 	} | ||||
|   | ||||
| @@ -25,11 +25,8 @@ import ( | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"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" | ||||
| ) | ||||
|  | ||||
| @@ -1444,9 +1441,8 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	testCases := map[string]struct { | ||||
| 		ingressClass                    *networking.IngressClass | ||||
| 		expectedErrs                    field.ErrorList | ||||
| 		enableNamespaceScopedParamsGate bool | ||||
| 		ingressClass *networking.IngressClass | ||||
| 		expectedErrs field.ErrorList | ||||
| 	}{ | ||||
| 		"valid name, valid controller": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar"), | ||||
| @@ -1470,25 +1466,25 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 		}, | ||||
| 		"valid name, valid controller, valid params": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "foo", "bar", nil, nil)), | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "foo", "bar", utilpointer.StringPtr("Cluster"), nil)), | ||||
| 			), | ||||
| 			expectedErrs: field.ErrorList{}, | ||||
| 		}, | ||||
| 		"valid name, valid controller, invalid params (no kind)": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "", "bar", nil, nil)), | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "", "bar", utilpointer.StringPtr("Cluster"), nil)), | ||||
| 			), | ||||
| 			expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.kind"), "kind is required")}, | ||||
| 		}, | ||||
| 		"valid name, valid controller, invalid params (no name)": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "foo", "", nil, nil)), | ||||
| 				setParams(makeIngressClassParams(utilpointer.StringPtr("example.com"), "foo", "", utilpointer.StringPtr("Cluster"), nil)), | ||||
| 			), | ||||
| 			expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.name"), "name is required")}, | ||||
| 		}, | ||||
| 		"valid name, valid controller, invalid params (bad kind)": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo/", "bar", nil, nil)), | ||||
| 				setParams(makeIngressClassParams(nil, "foo/", "bar", utilpointer.StringPtr("Cluster"), nil)), | ||||
| 			), | ||||
| 			expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.kind"), "foo/", "may not contain '/'")}, | ||||
| 		}, | ||||
| @@ -1496,7 +1492,6 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("bad-scope"), nil)), | ||||
| 			), | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 			expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"), | ||||
| 				"bad-scope", []string{"Cluster", "Namespace"})}, | ||||
| 		}, | ||||
| @@ -1504,14 +1499,12 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("Namespace"), utilpointer.StringPtr("foo-ns"))), | ||||
| 			), | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 			expectedErrs:                    field.ErrorList{}, | ||||
| 			expectedErrs: field.ErrorList{}, | ||||
| 		}, | ||||
| 		"valid name, valid controller, valid scope, invalid namespace": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("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', "+ | ||||
| @@ -1521,14 +1514,12 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("Cluster"), nil)), | ||||
| 			), | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 			expectedErrs:                    field.ErrorList{}, | ||||
| 			expectedErrs: field.ErrorList{}, | ||||
| 		}, | ||||
| 		"namespace not set when scope is Namespace": { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("Namespace"), nil)), | ||||
| 			), | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 			expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.namespace"), | ||||
| 				"`parameters.scope` is set to 'Namespace'")}, | ||||
| 		}, | ||||
| @@ -1536,7 +1527,6 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("Cluster"), utilpointer.StringPtr("foo-ns"))), | ||||
| 			), | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 			expectedErrs: field.ErrorList{field.Forbidden(field.NewPath("spec.parameters.namespace"), | ||||
| 				"`parameters.scope` is set to 'Cluster'")}, | ||||
| 		}, | ||||
| @@ -1544,38 +1534,13 @@ func TestValidateIngressClass(t *testing.T) { | ||||
| 			ingressClass: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("Cluster"), 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: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", utilpointer.StringPtr("bad-scope"), nil)), | ||||
| 			), | ||||
| 			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: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", nil, nil)), | ||||
| 			), | ||||
| 			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: makeValidIngressClass("test123", "foo.co/bar", | ||||
| 				setParams(makeIngressClassParams(nil, "foo", "bar", nil, 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) { | ||||
|   | ||||
| @@ -630,6 +630,7 @@ const ( | ||||
| 	// kep: http://kep.k8s.io/2365 | ||||
| 	// alpha: v1.21 | ||||
| 	// beta: v1.22 | ||||
| 	// GA: v1.23 | ||||
| 	// | ||||
| 	// Enable Scope and Namespace fields on IngressClassParametersReference. | ||||
| 	IngressClassNamespacedParams featuregate.Feature = "IngressClassNamespacedParams" | ||||
| @@ -891,7 +892,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS | ||||
| 	TopologyAwareHints:                             {Default: false, PreRelease: featuregate.Alpha}, | ||||
| 	PodAffinityNamespaceSelector:                   {Default: true, PreRelease: featuregate.Beta}, | ||||
| 	ServiceLoadBalancerClass:                       {Default: true, PreRelease: featuregate.Beta}, | ||||
| 	IngressClassNamespacedParams:                   {Default: true, PreRelease: featuregate.Beta}, | ||||
| 	IngressClassNamespacedParams:                   {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.24 | ||||
| 	ServiceInternalTrafficPolicy:                   {Default: true, PreRelease: featuregate.Beta}, | ||||
| 	LogarithmicScaleDown:                           {Default: true, PreRelease: featuregate.Beta}, | ||||
| 	SuspendJob:                                     {Default: true, PreRelease: featuregate.Beta}, | ||||
|   | ||||
| @@ -23,11 +23,9 @@ import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||
| 	"k8s.io/apiserver/pkg/storage/names" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	"k8s.io/kubernetes/pkg/api/legacyscheme" | ||||
| 	"k8s.io/kubernetes/pkg/apis/networking" | ||||
| 	"k8s.io/kubernetes/pkg/apis/networking/validation" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| ) | ||||
|  | ||||
| // ingressClassStrategy implements verification logic for IngressClass | ||||
| @@ -51,10 +49,6 @@ func (ingressClassStrategy) NamespaceScoped() bool { | ||||
| func (ingressClassStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { | ||||
| 	ingressClass := obj.(*networking.IngressClass) | ||||
| 	ingressClass.Generation = 1 | ||||
|  | ||||
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) { | ||||
| 		dropIngressClassParametersReferenceScope(ingressClass) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PrepareForUpdate clears fields that are not allowed to be set by end users on | ||||
| @@ -68,10 +62,6 @@ func (ingressClassStrategy) PrepareForUpdate(ctx context.Context, obj, old runti | ||||
| 	if !apiequality.Semantic.DeepEqual(oldIngressClass.Spec, newIngressClass.Spec) { | ||||
| 		newIngressClass.Generation = oldIngressClass.Generation + 1 | ||||
| 	} | ||||
|  | ||||
| 	if !utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) && !scopeInUse(oldIngressClass) { | ||||
| 		dropIngressClassParametersReferenceScope(newIngressClass) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Validate validates a new IngressClass. | ||||
| @@ -113,15 +103,3 @@ func (ingressClassStrategy) WarningsOnUpdate(ctx context.Context, obj, old runti | ||||
| func (ingressClassStrategy) AllowUnconditionalUpdate() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func scopeInUse(ingressClass *networking.IngressClass) bool { | ||||
| 	return ingressClass.Spec.Parameters != nil && ingressClass.Spec.Parameters.Scope != nil | ||||
| } | ||||
|  | ||||
| // Drops Scope and Namespace field from IngressClass's parameters. | ||||
| func dropIngressClassParametersReferenceScope(ingressClass *networking.IngressClass) { | ||||
| 	if ingressClass.Spec.Parameters != nil { | ||||
| 		ingressClass.Spec.Parameters.Scope = nil | ||||
| 		ingressClass.Spec.Parameters.Namespace = nil | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -19,15 +19,9 @@ package ingressclass | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	apiequality "k8s.io/apimachinery/pkg/api/equality" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	genericapirequest "k8s.io/apiserver/pkg/endpoints/request" | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	"k8s.io/kubernetes/pkg/apis/networking" | ||||
| 	"k8s.io/kubernetes/pkg/features" | ||||
| 	utilpointer "k8s.io/utils/pointer" | ||||
| ) | ||||
|  | ||||
| func TestIngressClassStrategy(t *testing.T) { | ||||
| @@ -76,282 +70,3 @@ func TestIngressClassStrategy(t *testing.T) { | ||||
| 		t.Errorf("Expected error from update validation for IngressClass, got none") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestIngressClassPrepareForCreate(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                            string | ||||
| 		original                        *networking.IngressClass | ||||
| 		expected                        *networking.IngressClass | ||||
| 		enableNamespaceScopedParamsGate bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "cluster scope is removed when feature is not enabled", | ||||
| 			original: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:  "k", | ||||
| 						Name:  "n", | ||||
| 						Scope: utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeCluster), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "namespace scope and namespace fields are removed when feature is not enabled", | ||||
| 			original: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "cluster scope is not removed when feature is enabled", | ||||
| 			original: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:  "k", | ||||
| 						Name:  "n", | ||||
| 						Scope: utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeCluster), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:  "k", | ||||
| 						Name:  "n", | ||||
| 						Scope: utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeCluster), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "namespace scope and namespace fields are not removed when feature is enabled", | ||||
| 			original: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	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)() | ||||
| 			ctx := genericapirequest.NewDefaultContext() | ||||
| 			Strategy.PrepareForCreate(ctx, runtime.Object(original)) | ||||
| 			if !apiequality.Semantic.DeepEqual(original.Spec, expected.Spec) { | ||||
| 				t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", original.Spec, expected.Spec) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestIngressClassPrepareForUpdate(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name                            string | ||||
| 		newIngressClass                 *networking.IngressClass | ||||
| 		oldIngressClass                 *networking.IngressClass | ||||
| 		expected                        *networking.IngressClass | ||||
| 		enableNamespaceScopedParamsGate bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "scope can be updated if already set when feature is disabled", | ||||
| 			newIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			oldIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:  "k", | ||||
| 						Name:  "n", | ||||
| 						Scope: utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeCluster), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "scope is removed if not already set previously when feature is disabled", | ||||
| 			newIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			oldIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "scope can be set when feature is enabled", | ||||
| 			newIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			oldIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "scope can be removed when feature is enabled", | ||||
| 			newIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			oldIngressClass: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind:      "k", | ||||
| 						Name:      "n", | ||||
| 						Scope:     utilpointer.StringPtr(networking.IngressClassParametersReferenceScopeNamespace), | ||||
| 						Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expected: &networking.IngressClass{ | ||||
| 				Spec: networking.IngressClassSpec{ | ||||
| 					Controller: "controller", | ||||
| 					Parameters: &networking.IngressClassParametersReference{ | ||||
| 						Kind: "k", | ||||
| 						Name: "n", | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			enableNamespaceScopedParamsGate: true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IngressClassNamespacedParams, test.enableNamespaceScopedParamsGate)() | ||||
| 			ctx := genericapirequest.NewDefaultContext() | ||||
| 			Strategy.PrepareForUpdate(ctx, runtime.Object(test.newIngressClass), runtime.Object(test.oldIngressClass)) | ||||
| 			if !apiequality.Semantic.DeepEqual(test.newIngressClass.Spec, test.expected.Spec) { | ||||
| 				t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", test.newIngressClass.Spec, test.expected.Spec) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -167,16 +167,13 @@ message IngressClassParametersReference { | ||||
|  | ||||
|   // 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 | ||||
|   optional string scope = 4; | ||||
|  | ||||
|   // 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 | ||||
|   optional string namespace = 5; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -534,15 +534,12 @@ type IngressClassParametersReference struct { | ||||
| 	Name string `json:"name" protobuf:"bytes,3,opt,name=name"` | ||||
| 	// 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 `json:"scope" protobuf:"bytes,4,opt,name=scope"` | ||||
| 	// 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 `json:"namespace,omitempty" protobuf:"bytes,5,opt,name=namespace"` | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -103,7 +103,7 @@ var map_IngressClassParametersReference = map[string]string{ | ||||
| 	"apiGroup":  "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.", | ||||
| 	"kind":      "Kind is the type of resource being referenced.", | ||||
| 	"name":      "Name is the name of resource being referenced.", | ||||
| 	"scope":     "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.", | ||||
| 	"scope":     "Scope represents if this refers to a cluster or namespace scoped resource. This may be set to \"Cluster\" (default) or \"Namespace\".", | ||||
| 	"namespace": "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\".", | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -154,16 +154,12 @@ message IngressClassParametersReference { | ||||
|  | ||||
|   // 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 | ||||
|   optional string scope = 4; | ||||
|  | ||||
|   // 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 | ||||
|   optional string namespace = 5; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -337,15 +337,11 @@ type IngressClassParametersReference struct { | ||||
| 	Name string `json:"name" protobuf:"bytes,3,opt,name=name"` | ||||
| 	// 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 `json:"scope" protobuf:"bytes,4,opt,name=scope"` | ||||
| 	// 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 `json:"namespace,omitempty" protobuf:"bytes,5,opt,name=namespace"` | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -94,7 +94,7 @@ var map_IngressClassParametersReference = map[string]string{ | ||||
| 	"apiGroup":  "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.", | ||||
| 	"kind":      "Kind is the type of resource being referenced.", | ||||
| 	"name":      "Name is the name of resource being referenced.", | ||||
| 	"scope":     "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.", | ||||
| 	"scope":     "Scope represents if this refers to a cluster or namespace scoped resource. This may be set to \"Cluster\" (default) or \"Namespace\".", | ||||
| 	"namespace": "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\".", | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -32,6 +32,7 @@ import ( | ||||
| 	clientset "k8s.io/client-go/kubernetes" | ||||
| 	"k8s.io/kubernetes/test/e2e/framework" | ||||
| 	"k8s.io/kubernetes/test/e2e/network/common" | ||||
| 	utilpointer "k8s.io/utils/pointer" | ||||
|  | ||||
| 	"github.com/onsi/ginkgo" | ||||
| ) | ||||
| @@ -96,6 +97,43 @@ var _ = common.SIGDescribe("IngressClass [Feature:Ingress]", func() { | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	ginkgo.It("should allow IngressClass to have Namespace-scoped parameters [Serial]", func() { | ||||
| 		ingressClass := &networkingv1.IngressClass{ | ||||
| 			ObjectMeta: metav1.ObjectMeta{ | ||||
| 				Name: "ingressclass1", | ||||
| 				Labels: map[string]string{ | ||||
| 					"ingressclass":  f.UniqueName, | ||||
| 					"special-label": "generic", | ||||
| 				}, | ||||
| 			}, | ||||
| 			Spec: networkingv1.IngressClassSpec{ | ||||
| 				Controller: "example.com/controller", | ||||
| 				Parameters: &networkingv1.IngressClassParametersReference{ | ||||
| 					Scope:     utilpointer.StringPtr("Namespace"), | ||||
| 					Namespace: utilpointer.StringPtr("foo-ns"), | ||||
| 					Kind:      "fookind", | ||||
| 					Name:      "fooname", | ||||
| 					APIGroup:  utilpointer.StringPtr("example.com"), | ||||
| 				}, | ||||
| 			}, | ||||
| 		} | ||||
| 		createdIngressClass, err := cs.NetworkingV1().IngressClasses().Create(context.TODO(), ingressClass, metav1.CreateOptions{}) | ||||
| 		framework.ExpectNoError(err) | ||||
| 		defer deleteIngressClass(cs, createdIngressClass.Name) | ||||
|  | ||||
| 		if createdIngressClass.Spec.Parameters == nil { | ||||
| 			framework.Failf("Expected IngressClass.spec.parameters to be set") | ||||
| 		} | ||||
| 		scope := "" | ||||
| 		if createdIngressClass.Spec.Parameters.Scope != nil { | ||||
| 			scope = *createdIngressClass.Spec.Parameters.Scope | ||||
| 		} | ||||
|  | ||||
| 		if scope != "Namespace" { | ||||
| 			framework.Failf("Expected IngressClass.spec.parameters.scope to be set to 'Namespace', got %v", scope) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| }) | ||||
|  | ||||
| func createIngressClass(cs clientset.Interface, name string, isDefault bool, uniqueName string) (*networkingv1.IngressClass, error) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Prow Robot
					Kubernetes Prow Robot