diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 84c720a8117..231d222f8bc 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -11944,7 +11944,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" } }, diff --git a/pkg/apis/networking/types.go b/pkg/apis/networking/types.go index a3f68bd07c2..5f87d99c53b 100644 --- a/pkg/apis/networking/types.go +++ b/pkg/apis/networking/types.go @@ -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 } diff --git a/pkg/apis/networking/v1/defaults.go b/pkg/apis/networking/v1/defaults.go index 9ce991e8c75..b021083bfc8 100644 --- a/pkg/apis/networking/v1/defaults.go +++ b/pkg/apis/networking/v1/defaults.go @@ -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) } diff --git a/pkg/apis/networking/v1/defaults_test.go b/pkg/apis/networking/v1/defaults_test.go index 023b21e5f34..308fb087939 100644 --- a/pkg/apis/networking/v1/defaults_test.go +++ b/pkg/apis/networking/v1/defaults_test.go @@ -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 { diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go index 6ce045d7a39..078456933db 100644 --- a/pkg/apis/networking/validation/validation.go +++ b/pkg/apis/networking/validation/validation.go @@ -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 } diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go index 2201d81c093..ca692b9dfb3 100644 --- a/pkg/apis/networking/validation/validation_test.go +++ b/pkg/apis/networking/validation/validation_test.go @@ -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) { diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index fe36edd4a62..90036be3003 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -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" @@ -882,7 +883,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}, diff --git a/pkg/registry/networking/ingressclass/strategy.go b/pkg/registry/networking/ingressclass/strategy.go index 3434a794340..4e1b22688ca 100644 --- a/pkg/registry/networking/ingressclass/strategy.go +++ b/pkg/registry/networking/ingressclass/strategy.go @@ -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 - } -} diff --git a/pkg/registry/networking/ingressclass/strategy_test.go b/pkg/registry/networking/ingressclass/strategy_test.go index 61c88e66614..0f8cbaf8b85 100644 --- a/pkg/registry/networking/ingressclass/strategy_test.go +++ b/pkg/registry/networking/ingressclass/strategy_test.go @@ -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) - } - }) - } -} diff --git a/staging/src/k8s.io/api/networking/v1/generated.proto b/staging/src/k8s.io/api/networking/v1/generated.proto index 8559e92d73b..e98ab179e2c 100644 --- a/staging/src/k8s.io/api/networking/v1/generated.proto +++ b/staging/src/k8s.io/api/networking/v1/generated.proto @@ -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; } diff --git a/staging/src/k8s.io/api/networking/v1/types.go b/staging/src/k8s.io/api/networking/v1/types.go index 7edbcd5979e..e429dc5355b 100644 --- a/staging/src/k8s.io/api/networking/v1/types.go +++ b/staging/src/k8s.io/api/networking/v1/types.go @@ -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"` } diff --git a/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go index 49b38bd0492..38bd9c502b9 100644 --- a/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go @@ -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\".", } diff --git a/staging/src/k8s.io/api/networking/v1beta1/generated.proto b/staging/src/k8s.io/api/networking/v1beta1/generated.proto index 5479ac482b3..ef8cceff437 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/networking/v1beta1/generated.proto @@ -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; } diff --git a/staging/src/k8s.io/api/networking/v1beta1/types.go b/staging/src/k8s.io/api/networking/v1beta1/types.go index 4646e90b64c..1bfdcd0915b 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/types.go +++ b/staging/src/k8s.io/api/networking/v1beta1/types.go @@ -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"` } diff --git a/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go index 88c130e89b4..79c42a6ba4b 100644 --- a/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go @@ -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\".", } diff --git a/test/e2e/network/ingressclass.go b/test/e2e/network/ingressclass.go index 1939b19f9d0..b4f33397ab7 100644 --- a/test/e2e/network/ingressclass.go +++ b/test/e2e/network/ingressclass.go @@ -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) {