Skip to content

Commit ab13cf1

Browse files
authored
Merge pull request kubernetes#81691 from MrHohn/svc-finalizer-beta
Promote service load balancer finalizer to Beta
2 parents 467bdcb + 0a7c085 commit ab13cf1

File tree

6 files changed

+79
-86
lines changed

6 files changed

+79
-86
lines changed

pkg/controller/service/service_controller_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ func TestProcessServiceCreateOrUpdate(t *testing.T) {
618618
}
619619

620620
// TestProcessServiceCreateOrUpdateK8sError tests processServiceCreateOrUpdate
621-
// with various kubernetes errors.
621+
// with various kubernetes errors when patching status.
622622
func TestProcessServiceCreateOrUpdateK8sError(t *testing.T) {
623623
svcName := "svc-k8s-err"
624624
conflictErr := apierrors.NewConflict(schema.GroupResource{}, svcName, errors.New("object conflict"))
@@ -644,6 +644,8 @@ func TestProcessServiceCreateOrUpdateK8sError(t *testing.T) {
644644
for _, tc := range testCases {
645645
t.Run(tc.desc, func(t *testing.T) {
646646
svc := newService(svcName, types.UID("123"), v1.ServiceTypeLoadBalancer)
647+
// Preset finalizer so k8s error only happens when patching status.
648+
svc.Finalizers = []string{servicehelper.LoadBalancerCleanupFinalizer}
647649
controller, _, client := newController()
648650
client.PrependReactor("patch", "services", func(action core.Action) (bool, runtime.Object, error) {
649651
return true, nil, tc.k8sErr

pkg/features/kube_features.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ const (
427427
deprecatedGCERegionalPersistentDisk featuregate.Feature = "GCERegionalPersistentDisk"
428428

429429
// owner: @MrHohn
430-
// alpha: v1.15
430+
// beta: v1.16
431431
//
432432
// Enables Finalizer Protection for Service LoadBalancers.
433433
ServiceLoadBalancerFinalizer featuregate.Feature = "ServiceLoadBalancerFinalizer"
@@ -539,7 +539,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
539539
KubeletPodResources: {Default: true, PreRelease: featuregate.Beta},
540540
WindowsGMSA: {Default: false, PreRelease: featuregate.Alpha},
541541
WindowsRunAsUserName: {Default: false, PreRelease: featuregate.Alpha},
542-
ServiceLoadBalancerFinalizer: {Default: false, PreRelease: featuregate.Alpha},
542+
ServiceLoadBalancerFinalizer: {Default: true, PreRelease: featuregate.Beta},
543543
LocalStorageCapacityIsolationFSQuotaMonitoring: {Default: false, PreRelease: featuregate.Alpha},
544544
NonPreemptingPriority: {Default: false, PreRelease: featuregate.Alpha},
545545
VolumePVCDataSource: {Default: false, PreRelease: featuregate.Alpha},

test/e2e/framework/service/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ go_library(
3434
"//staging/src/k8s.io/client-go/rest:go_default_library",
3535
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
3636
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
37+
"//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library",
3738
"//test/e2e/framework:go_default_library",
3839
"//test/e2e/framework/log:go_default_library",
3940
"//test/e2e/framework/node:go_default_library",

test/e2e/framework/service/wait.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import (
2020
"context"
2121
"fmt"
2222

23-
"github.com/onsi/ginkgo"
23+
apierrors "k8s.io/apimachinery/pkg/api/errors"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425
"k8s.io/apimachinery/pkg/util/wait"
2526
clientset "k8s.io/client-go/kubernetes"
27+
servicehelper "k8s.io/cloud-provider/service/helpers"
2628
"k8s.io/kubernetes/test/e2e/framework"
2729
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
30+
31+
"github.com/onsi/ginkgo"
2832
)
2933

3034
// WaitForServiceResponding waits for the service to be responding.
@@ -63,3 +67,52 @@ func WaitForServiceResponding(c clientset.Interface, ns, name string) error {
6367
return true, nil
6468
})
6569
}
70+
71+
// WaitForServiceDeletedWithFinalizer waits for the service with finalizer to be deleted.
72+
func WaitForServiceDeletedWithFinalizer(cs clientset.Interface, namespace, name string) {
73+
ginkgo.By("Delete service with finalizer")
74+
if err := cs.CoreV1().Services(namespace).Delete(name, nil); err != nil {
75+
e2elog.Failf("Failed to delete service %s/%s", namespace, name)
76+
}
77+
78+
ginkgo.By("Wait for service to disappear")
79+
if pollErr := wait.PollImmediate(LoadBalancerPollInterval, GetServiceLoadBalancerCreationTimeout(cs), func() (bool, error) {
80+
svc, err := cs.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
81+
if err != nil {
82+
if apierrors.IsNotFound(err) {
83+
e2elog.Logf("Service %s/%s is gone.", namespace, name)
84+
return true, nil
85+
}
86+
return false, err
87+
}
88+
e2elog.Logf("Service %s/%s still exists with finalizers: %v", namespace, name, svc.Finalizers)
89+
return false, nil
90+
}); pollErr != nil {
91+
e2elog.Failf("Failed to wait for service to disappear: %v", pollErr)
92+
}
93+
}
94+
95+
// WaitForServiceUpdatedWithFinalizer waits for the service to be updated to have or
96+
// don't have a finalizer.
97+
func WaitForServiceUpdatedWithFinalizer(cs clientset.Interface, namespace, name string, hasFinalizer bool) {
98+
ginkgo.By(fmt.Sprintf("Wait for service to hasFinalizer=%t", hasFinalizer))
99+
if pollErr := wait.PollImmediate(LoadBalancerPollInterval, GetServiceLoadBalancerCreationTimeout(cs), func() (bool, error) {
100+
svc, err := cs.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
101+
if err != nil {
102+
return false, err
103+
}
104+
foundFinalizer := false
105+
for _, finalizer := range svc.Finalizers {
106+
if finalizer == servicehelper.LoadBalancerCleanupFinalizer {
107+
foundFinalizer = true
108+
}
109+
}
110+
if foundFinalizer != hasFinalizer {
111+
e2elog.Logf("Service %s/%s hasFinalizer=%t, want %t", namespace, name, foundFinalizer, hasFinalizer)
112+
return false, nil
113+
}
114+
return true, nil
115+
}); pollErr != nil {
116+
e2elog.Failf("Failed to wait for service to hasFinalizer=%t: %v", hasFinalizer, pollErr)
117+
}
118+
}

test/e2e/network/service.go

Lines changed: 6 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
compute "google.golang.org/api/compute/v1"
3030

3131
v1 "k8s.io/api/core/v1"
32-
apierrors "k8s.io/apimachinery/pkg/api/errors"
3332
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3433
"k8s.io/apimachinery/pkg/labels"
3534
"k8s.io/apimachinery/pkg/util/intstr"
@@ -1958,39 +1957,13 @@ var _ = SIGDescribe("Services", func() {
19581957
}
19591958
})
19601959

1961-
// This test verifies if service load balancer cleanup finalizer can be removed
1962-
// when feature gate isn't enabled on the cluster.
1963-
// This ensures downgrading from higher version cluster will not break LoadBalancer
1964-
// type service.
1965-
ginkgo.It("should remove load balancer cleanup finalizer when service is deleted [Slow]", func() {
1966-
jig := e2eservice.NewTestJig(cs, "lb-remove-finalizer")
1967-
1968-
ginkgo.By("Create load balancer service")
1969-
svc := jig.CreateTCPServiceOrFail(f.Namespace.Name, func(svc *v1.Service) {
1970-
svc.Spec.Type = v1.ServiceTypeLoadBalancer
1971-
})
1972-
1973-
defer func() {
1974-
waitForServiceDeletedWithFinalizer(cs, svc.Namespace, svc.Name)
1975-
}()
1976-
1977-
ginkgo.By("Wait for load balancer to serve traffic")
1978-
svc = jig.WaitForLoadBalancerOrFail(svc.Namespace, svc.Name, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
1979-
1980-
ginkgo.By("Manually add load balancer cleanup finalizer to service")
1981-
svc.Finalizers = append(svc.Finalizers, "service.kubernetes.io/load-balancer-cleanup")
1982-
if _, err := cs.CoreV1().Services(svc.Namespace).Update(svc); err != nil {
1983-
e2elog.Failf("Failed to add finalizer to service %s/%s: %v", svc.Namespace, svc.Name, err)
1984-
}
1985-
})
1986-
19871960
// This test verifies if service load balancer cleanup finalizer is properly
19881961
// handled during service lifecycle.
19891962
// 1. Create service with type=LoadBalancer. Finalizer should be added.
19901963
// 2. Update service to type=ClusterIP. Finalizer should be removed.
19911964
// 3. Update service to type=LoadBalancer. Finalizer should be added.
19921965
// 4. Delete service with type=LoadBalancer. Finalizer should be removed.
1993-
ginkgo.It("should handle load balancer cleanup finalizer for service [Slow] [Feature:ServiceFinalizer]", func() {
1966+
ginkgo.It("should handle load balancer cleanup finalizer for service [Slow]", func() {
19941967
jig := e2eservice.NewTestJig(cs, "lb-finalizer")
19951968

19961969
ginkgo.By("Create load balancer service")
@@ -1999,71 +1972,26 @@ var _ = SIGDescribe("Services", func() {
19991972
})
20001973

20011974
defer func() {
2002-
waitForServiceDeletedWithFinalizer(cs, svc.Namespace, svc.Name)
1975+
ginkgo.By("Check that service can be deleted with finalizer")
1976+
e2eservice.WaitForServiceDeletedWithFinalizer(cs, svc.Namespace, svc.Name)
20031977
}()
20041978

20051979
ginkgo.By("Wait for load balancer to serve traffic")
20061980
svc = jig.WaitForLoadBalancerOrFail(svc.Namespace, svc.Name, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
20071981

20081982
ginkgo.By("Check if finalizer presents on service with type=LoadBalancer")
2009-
waitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, true)
1983+
e2eservice.WaitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, true)
20101984

20111985
ginkgo.By("Check if finalizer is removed on service after changed to type=ClusterIP")
20121986
jig.ChangeServiceType(svc.Namespace, svc.Name, v1.ServiceTypeClusterIP, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
2013-
waitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, false)
1987+
e2eservice.WaitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, false)
20141988

20151989
ginkgo.By("Check if finalizer is added back to service after changed to type=LoadBalancer")
20161990
jig.ChangeServiceType(svc.Namespace, svc.Name, v1.ServiceTypeLoadBalancer, e2eservice.GetServiceLoadBalancerCreationTimeout(cs))
2017-
waitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, true)
1991+
e2eservice.WaitForServiceUpdatedWithFinalizer(cs, svc.Namespace, svc.Name, true)
20181992
})
20191993
})
20201994

2021-
func waitForServiceDeletedWithFinalizer(cs clientset.Interface, namespace, name string) {
2022-
ginkgo.By("Delete service with finalizer")
2023-
if err := cs.CoreV1().Services(namespace).Delete(name, nil); err != nil {
2024-
e2elog.Failf("Failed to delete service %s/%s", namespace, name)
2025-
}
2026-
2027-
ginkgo.By("Wait for service to disappear")
2028-
if pollErr := wait.PollImmediate(e2eservice.LoadBalancerPollInterval, e2eservice.GetServiceLoadBalancerCreationTimeout(cs), func() (bool, error) {
2029-
svc, err := cs.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
2030-
if err != nil {
2031-
if apierrors.IsNotFound(err) {
2032-
e2elog.Logf("Service %s/%s is gone.", namespace, name)
2033-
return true, nil
2034-
}
2035-
return false, err
2036-
}
2037-
e2elog.Logf("Service %s/%s still exists with finalizers: %v", namespace, name, svc.Finalizers)
2038-
return false, nil
2039-
}); pollErr != nil {
2040-
e2elog.Failf("Failed to wait for service to disappear: %v", pollErr)
2041-
}
2042-
}
2043-
2044-
func waitForServiceUpdatedWithFinalizer(cs clientset.Interface, namespace, name string, hasFinalizer bool) {
2045-
ginkgo.By(fmt.Sprintf("Wait for service to hasFinalizer=%t", hasFinalizer))
2046-
if pollErr := wait.PollImmediate(e2eservice.LoadBalancerPollInterval, e2eservice.GetServiceLoadBalancerCreationTimeout(cs), func() (bool, error) {
2047-
svc, err := cs.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
2048-
if err != nil {
2049-
return false, err
2050-
}
2051-
foundFinalizer := false
2052-
for _, finalizer := range svc.Finalizers {
2053-
if finalizer == "service.kubernetes.io/load-balancer-cleanup" {
2054-
foundFinalizer = true
2055-
}
2056-
}
2057-
if foundFinalizer != hasFinalizer {
2058-
e2elog.Logf("Service %s/%s hasFinalizer=%t, want %t", namespace, name, foundFinalizer, hasFinalizer)
2059-
return false, nil
2060-
}
2061-
return true, nil
2062-
}); pollErr != nil {
2063-
e2elog.Failf("Failed to wait for service to hasFinalizer=%t: %v", hasFinalizer, pollErr)
2064-
}
2065-
}
2066-
20671995
// TODO: Get rid of [DisabledForLargeClusters] tag when issue #56138 is fixed.
20681996
var _ = SIGDescribe("ESIPP [Slow] [DisabledForLargeClusters]", func() {
20691997
f := framework.NewDefaultFramework("esipp")

test/e2e/upgrades/services.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ func (t *ServiceUpgradeTest) Setup(f *framework.Framework) {
8484
func (t *ServiceUpgradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade UpgradeType) {
8585
switch upgrade {
8686
case MasterUpgrade, ClusterUpgrade:
87-
t.test(f, done, true)
87+
t.test(f, done, true, true)
8888
case NodeUpgrade:
8989
// Node upgrades should test during disruption only on GCE/GKE for now.
90-
t.test(f, done, shouldTestPDBs())
90+
t.test(f, done, shouldTestPDBs(), false)
9191
default:
92-
t.test(f, done, false)
92+
t.test(f, done, false, false)
9393
}
9494
}
9595

@@ -98,7 +98,7 @@ func (t *ServiceUpgradeTest) Teardown(f *framework.Framework) {
9898
// rely on the namespace deletion to clean up everything
9999
}
100100

101-
func (t *ServiceUpgradeTest) test(f *framework.Framework, done <-chan struct{}, testDuringDisruption bool) {
101+
func (t *ServiceUpgradeTest) test(f *framework.Framework, done <-chan struct{}, testDuringDisruption, testFinalizer bool) {
102102
if testDuringDisruption {
103103
// Continuous validation
104104
ginkgo.By("continuously hitting the pod through the service's LoadBalancer")
@@ -115,4 +115,13 @@ func (t *ServiceUpgradeTest) test(f *framework.Framework, done <-chan struct{},
115115
ginkgo.By("hitting the pod through the service's LoadBalancer")
116116
t.jig.TestReachableHTTP(t.tcpIngressIP, t.svcPort, e2eservice.LoadBalancerLagTimeoutDefault)
117117
t.jig.SanityCheckService(t.tcpService, v1.ServiceTypeLoadBalancer)
118+
119+
if testFinalizer {
120+
defer func() {
121+
ginkgo.By("Check that service can be deleted with finalizer")
122+
e2eservice.WaitForServiceDeletedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name)
123+
}()
124+
ginkgo.By("Check that finalizer is present on loadBalancer type service")
125+
e2eservice.WaitForServiceUpdatedWithFinalizer(t.jig.Client, t.tcpService.Namespace, t.tcpService.Name, true)
126+
}
118127
}

0 commit comments

Comments
 (0)