From fe161579e206f177186ab88681ff5ca8cb615397 Mon Sep 17 00:00:00 2001 From: Xudong Liu Date: Wed, 23 Jun 2021 16:18:58 -0700 Subject: [PATCH] Add integration test to promote serviceloadbalancerclass feature to beta. --- pkg/features/kube_features.go | 5 +- test/integration/service/loadbalancer_test.go | 129 ++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 70692586094..b9587bd2266 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -649,9 +649,10 @@ const ( // Allow specifying NamespaceSelector in PodAffinityTerm. PodAffinityNamespaceSelector featuregate.Feature = "PodAffinityNamespaceSelector" - // owner: @andrewsykim @xudongliuharold + // owner: @andrewsykim @XudongLiuHarold // kep: http://kep.k8s.io/1959 // alpha: v1.21 + // beta: v1.22 // // Enable support multiple Service "type: LoadBalancer" implementations in a cluster by specifying LoadBalancerClass ServiceLoadBalancerClass featuregate.Feature = "ServiceLoadBalancerClass" @@ -816,7 +817,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS PodDeletionCost: {Default: true, PreRelease: featuregate.Beta}, TopologyAwareHints: {Default: false, PreRelease: featuregate.Alpha}, PodAffinityNamespaceSelector: {Default: true, PreRelease: featuregate.Beta}, - ServiceLoadBalancerClass: {Default: false, PreRelease: featuregate.Alpha}, + ServiceLoadBalancerClass: {Default: true, PreRelease: featuregate.Beta}, LogarithmicScaleDown: {Default: false, PreRelease: featuregate.Alpha}, IngressClassNamespacedParams: {Default: false, PreRelease: featuregate.Alpha}, ServiceInternalTrafficPolicy: {Default: false, PreRelease: featuregate.Alpha}, diff --git a/test/integration/service/loadbalancer_test.go b/test/integration/service/loadbalancer_test.go index 93c3ea2617b..d2075db0a28 100644 --- a/test/integration/service/loadbalancer_test.go +++ b/test/integration/service/loadbalancer_test.go @@ -23,8 +23,11 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" + servicecontroller "k8s.io/cloud-provider/controllers/service" + fakecloud "k8s.io/cloud-provider/fake" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/integration/framework" @@ -138,3 +141,129 @@ func serviceHasNodePorts(svc *corev1.Service) bool { return false } + +// Test_ServiceLoadBalancerEnableLoadBalancerClass tests that when a LoadBalancer +// type of service has spec.LoadBalancerClass set, cloud provider should not create default load balancer. +func Test_ServiceLoadBalancerEnableLoadBalancerClass(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceLoadBalancerClass, true)() + + controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig() + _, server, closeFn := framework.RunAnAPIServer(controlPlaneConfig) + defer closeFn() + + config := restclient.Config{Host: server.URL} + client, err := clientset.NewForConfig(&config) + if err != nil { + t.Fatalf("Error creating clientset: %v", err) + } + + ns := framework.CreateTestingNamespace("test-service-load-balancer-class", server, t) + defer framework.DeleteTestingNamespace(ns, server, t) + + controller, cloud, informer := newCloudProviderController(t, client) + + stopCh := make(chan struct{}) + informer.Start(stopCh) + go controller.Run(stopCh, 1) + defer close(stopCh) + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-load-balancer-class", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + Ports: []corev1.ServicePort{{ + Port: int32(80), + }}, + LoadBalancerClass: utilpointer.StringPtr("test.com/test"), + }, + } + + _, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating test service: %v", err) + } + + if len(cloud.Calls) > 0 { + t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls) + } +} + +// Test_ServiceLoadBalancerEnableLoadBalancerClassThenUpdateLoadBalancerClass tests that when a LoadBalancer +// type of service has spec.LoadBalancerClass set, it should be immutable as long as the service type +// is still LoadBalancer. +func Test_ServiceLoadBalancerEnableLoadBalancerClassThenUpdateLoadBalancerClass(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceLoadBalancerClass, true)() + + controlPlaneConfig := framework.NewIntegrationTestControlPlaneConfig() + _, server, closeFn := framework.RunAnAPIServer(controlPlaneConfig) + defer closeFn() + + config := restclient.Config{Host: server.URL} + client, err := clientset.NewForConfig(&config) + if err != nil { + t.Fatalf("Error creating clientset: %v", err) + } + + ns := framework.CreateTestingNamespace("test-service-immutable-load-balancer-class", server, t) + defer framework.DeleteTestingNamespace(ns, server, t) + + controller, cloud, informer := newCloudProviderController(t, client) + + stopCh := make(chan struct{}) + informer.Start(stopCh) + go controller.Run(stopCh, 1) + defer close(stopCh) + + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-load-balancer-class", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, + Ports: []corev1.ServicePort{{ + Port: int32(80), + }}, + LoadBalancerClass: utilpointer.StringPtr("test.com/test"), + }, + } + + service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error creating test service: %v", err) + } + + if len(cloud.Calls) > 0 { + t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls) + } + + service.Spec.LoadBalancerClass = utilpointer.StringPtr("test.com/update") + _, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{}) + if err == nil { + t.Fatal("Error updating test service load balancer class should throw error") + } + + if len(cloud.Calls) > 0 { + t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls) + } +} + +func newCloudProviderController(t *testing.T, client *clientset.Clientset) (*servicecontroller.Controller, *fakecloud.Cloud, informers.SharedInformerFactory) { + cloud := &fakecloud.Cloud{} + informerFactory := informers.NewSharedInformerFactory(client, 0) + serviceInformer := informerFactory.Core().V1().Services() + nodeInformer := informerFactory.Core().V1().Nodes() + + controller, err := servicecontroller.New(cloud, + client, + serviceInformer, + nodeInformer, + "test-cluster", + utilfeature.DefaultFeatureGate) + if err != nil { + t.Fatalf("Error creating cloud provider controller: %v", err) + } + cloud.Calls = nil // ignore any cloud calls made in init() + return controller, cloud, informerFactory +}