...

Source file src/k8s.io/kubernetes/test/integration/service/loadbalancer_test.go

Documentation: k8s.io/kubernetes/test/integration/service

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package service
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	"k8s.io/client-go/informers"
    32  	clientset "k8s.io/client-go/kubernetes"
    33  	servicecontroller "k8s.io/cloud-provider/controllers/service"
    34  	fakecloud "k8s.io/cloud-provider/fake"
    35  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    36  	controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers"
    37  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    38  	"k8s.io/kubernetes/pkg/features"
    39  	"k8s.io/kubernetes/test/integration/framework"
    40  	"k8s.io/utils/net"
    41  	utilpointer "k8s.io/utils/pointer"
    42  )
    43  
    44  // Test_ServiceLoadBalancerAllocateNodePorts tests that a Service with spec.allocateLoadBalancerNodePorts=false
    45  // does not allocate node ports for the Service.
    46  func Test_ServiceLoadBalancerDisableAllocateNodePorts(t *testing.T) {
    47  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
    48  	defer server.TearDownFn()
    49  
    50  	client, err := clientset.NewForConfig(server.ClientConfig)
    51  	if err != nil {
    52  		t.Fatalf("Error creating clientset: %v", err)
    53  	}
    54  
    55  	ns := framework.CreateNamespaceOrDie(client, "test-service-allocate-node-ports", t)
    56  	defer framework.DeleteNamespaceOrDie(client, ns, t)
    57  
    58  	service := &corev1.Service{
    59  		ObjectMeta: metav1.ObjectMeta{
    60  			Name: "test-123",
    61  		},
    62  		Spec: corev1.ServiceSpec{
    63  			Type:                          corev1.ServiceTypeLoadBalancer,
    64  			AllocateLoadBalancerNodePorts: utilpointer.Bool(false),
    65  			Ports: []corev1.ServicePort{{
    66  				Port: int32(80),
    67  			}},
    68  			Selector: map[string]string{
    69  				"foo": "bar",
    70  			},
    71  		},
    72  	}
    73  
    74  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
    75  	if err != nil {
    76  		t.Fatalf("Error creating test service: %v", err)
    77  	}
    78  
    79  	if serviceHasNodePorts(service) {
    80  		t.Error("found node ports when none was expected")
    81  	}
    82  }
    83  
    84  // Test_ServiceUpdateLoadBalancerAllocateNodePorts tests that a Service that is updated from ClusterIP to LoadBalancer
    85  // with spec.allocateLoadBalancerNodePorts=false does not allocate node ports for the Service
    86  func Test_ServiceUpdateLoadBalancerDisableAllocateNodePorts(t *testing.T) {
    87  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
    88  	defer server.TearDownFn()
    89  
    90  	client, err := clientset.NewForConfig(server.ClientConfig)
    91  	if err != nil {
    92  		t.Fatalf("Error creating clientset: %v", err)
    93  	}
    94  
    95  	ns := framework.CreateNamespaceOrDie(client, "test-service-allocate-node-ports", t)
    96  	defer framework.DeleteNamespaceOrDie(client, ns, t)
    97  
    98  	service := &corev1.Service{
    99  		ObjectMeta: metav1.ObjectMeta{
   100  			Name: "test-123",
   101  		},
   102  		Spec: corev1.ServiceSpec{
   103  			Type: corev1.ServiceTypeClusterIP,
   104  			Ports: []corev1.ServicePort{{
   105  				Port: int32(80),
   106  			}},
   107  			Selector: map[string]string{
   108  				"foo": "bar",
   109  			},
   110  		},
   111  	}
   112  
   113  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   114  	if err != nil {
   115  		t.Fatalf("Error creating test service: %v", err)
   116  	}
   117  
   118  	if serviceHasNodePorts(service) {
   119  		t.Error("found node ports when none was expected")
   120  	}
   121  
   122  	service.Spec.Type = corev1.ServiceTypeLoadBalancer
   123  	service.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
   124  	service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{})
   125  	if err != nil {
   126  		t.Fatalf("Error updating test service: %v", err)
   127  	}
   128  
   129  	if serviceHasNodePorts(service) {
   130  		t.Error("found node ports when none was expected")
   131  	}
   132  }
   133  
   134  // Test_ServiceLoadBalancerSwitchToDeallocatedNodePorts test that switching a Service
   135  // to spec.allocateLoadBalancerNodePorts=false, does not de-allocate existing node ports.
   136  func Test_ServiceLoadBalancerEnableThenDisableAllocatedNodePorts(t *testing.T) {
   137  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   138  	defer server.TearDownFn()
   139  
   140  	client, err := clientset.NewForConfig(server.ClientConfig)
   141  	if err != nil {
   142  		t.Fatalf("Error creating clientset: %v", err)
   143  	}
   144  
   145  	ns := framework.CreateNamespaceOrDie(client, "test-service-deallocate-node-ports", t)
   146  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   147  
   148  	service := &corev1.Service{
   149  		ObjectMeta: metav1.ObjectMeta{
   150  			Name: "test-123",
   151  		},
   152  		Spec: corev1.ServiceSpec{
   153  			Type:                          corev1.ServiceTypeLoadBalancer,
   154  			AllocateLoadBalancerNodePorts: utilpointer.Bool(true),
   155  			Ports: []corev1.ServicePort{{
   156  				Port: int32(80),
   157  			}},
   158  			Selector: map[string]string{
   159  				"foo": "bar",
   160  			},
   161  		},
   162  	}
   163  
   164  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   165  	if err != nil {
   166  		t.Fatalf("Error creating test service: %v", err)
   167  	}
   168  
   169  	if !serviceHasNodePorts(service) {
   170  		t.Error("expected node ports but found none")
   171  	}
   172  
   173  	service.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
   174  	service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{})
   175  	if err != nil {
   176  		t.Fatalf("Error updating test service: %v", err)
   177  	}
   178  
   179  	if !serviceHasNodePorts(service) {
   180  		t.Error("node ports were unexpectedly deallocated")
   181  	}
   182  }
   183  
   184  // Test_ServiceLoadBalancerDisableAllocatedNodePort test that switching a Service
   185  // to spec.allocateLoadBalancerNodePorts=false can de-allocate existing node ports.
   186  func Test_ServiceLoadBalancerDisableAllocatedNodePort(t *testing.T) {
   187  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   188  	defer server.TearDownFn()
   189  
   190  	client, err := clientset.NewForConfig(server.ClientConfig)
   191  	if err != nil {
   192  		t.Fatalf("Error creating clientset: %v", err)
   193  	}
   194  
   195  	ns := framework.CreateNamespaceOrDie(client, "test-service-deallocate-node-ports", t)
   196  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   197  
   198  	service := &corev1.Service{
   199  		ObjectMeta: metav1.ObjectMeta{
   200  			Name: "test-123",
   201  		},
   202  		Spec: corev1.ServiceSpec{
   203  			Type:                          corev1.ServiceTypeLoadBalancer,
   204  			AllocateLoadBalancerNodePorts: utilpointer.Bool(true),
   205  			Ports: []corev1.ServicePort{{
   206  				Port: int32(80),
   207  			}},
   208  			Selector: map[string]string{
   209  				"foo": "bar",
   210  			},
   211  		},
   212  	}
   213  
   214  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   215  	if err != nil {
   216  		t.Fatalf("Error creating test service: %v", err)
   217  	}
   218  
   219  	if !serviceHasNodePorts(service) {
   220  		t.Error("expected node ports but found none")
   221  	}
   222  
   223  	service.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
   224  	service.Spec.Ports[0].NodePort = 0
   225  	service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{})
   226  	if err != nil {
   227  		t.Fatalf("Error updating test service: %v", err)
   228  	}
   229  
   230  	if serviceHasNodePorts(service) {
   231  		t.Error("node ports were expected to be deallocated")
   232  	}
   233  }
   234  
   235  // Test_ServiceLoadBalancerDisableAllocatedNodePorts test that switching a Service
   236  // to spec.allocateLoadBalancerNodePorts=false can de-allocate one of existing node ports.
   237  func Test_ServiceLoadBalancerDisableAllocatedNodePorts(t *testing.T) {
   238  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   239  	defer server.TearDownFn()
   240  
   241  	client, err := clientset.NewForConfig(server.ClientConfig)
   242  	if err != nil {
   243  		t.Fatalf("Error creating clientset: %v", err)
   244  	}
   245  
   246  	ns := framework.CreateNamespaceOrDie(client, "test-service-deallocate-node-ports", t)
   247  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   248  
   249  	service := &corev1.Service{
   250  		ObjectMeta: metav1.ObjectMeta{
   251  			Name: "test-123",
   252  		},
   253  		Spec: corev1.ServiceSpec{
   254  			Type:                          corev1.ServiceTypeLoadBalancer,
   255  			AllocateLoadBalancerNodePorts: utilpointer.Bool(true),
   256  			Ports: []corev1.ServicePort{{
   257  				Name: "np-1",
   258  				Port: int32(80),
   259  			}, {
   260  				Name: "np-2",
   261  				Port: int32(81),
   262  			}},
   263  			Selector: map[string]string{
   264  				"foo": "bar",
   265  			},
   266  		},
   267  	}
   268  
   269  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   270  	if err != nil {
   271  		t.Fatalf("Error creating test service: %v", err)
   272  	}
   273  
   274  	if !serviceHasNodePorts(service) {
   275  		t.Error("expected node ports but found none")
   276  	}
   277  
   278  	service.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
   279  	service.Spec.Ports[0].NodePort = 0
   280  	service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{})
   281  	if err != nil {
   282  		t.Fatalf("Error updating test service: %v", err)
   283  	}
   284  
   285  	if service.Spec.Ports[0].NodePort != 0 {
   286  		t.Error("node ports[0] was expected to be deallocated")
   287  	}
   288  	if service.Spec.Ports[1].NodePort == 0 {
   289  		t.Error("node ports was not expected to be deallocated")
   290  	}
   291  }
   292  
   293  // Test_ServiceLoadBalancerDisableAllocatedNodePortsByPatch test that switching a Service
   294  // to spec.allocateLoadBalancerNodePorts=false with path can de-allocate one of existing node ports.
   295  func Test_ServiceLoadBalancerDisableAllocatedNodePortsByPatch(t *testing.T) {
   296  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   297  	defer server.TearDownFn()
   298  
   299  	client, err := clientset.NewForConfig(server.ClientConfig)
   300  	if err != nil {
   301  		t.Fatalf("Error creating clientset: %v", err)
   302  	}
   303  
   304  	ns := framework.CreateNamespaceOrDie(client, "test-service-deallocate-node-ports", t)
   305  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   306  
   307  	service := &corev1.Service{
   308  		ObjectMeta: metav1.ObjectMeta{
   309  			Name: "test-123",
   310  		},
   311  		Spec: corev1.ServiceSpec{
   312  			Type:                          corev1.ServiceTypeLoadBalancer,
   313  			AllocateLoadBalancerNodePorts: utilpointer.Bool(true),
   314  			Ports: []corev1.ServicePort{{
   315  				Name: "np-1",
   316  				Port: int32(80),
   317  			}, {
   318  				Name: "np-2",
   319  				Port: int32(81),
   320  			}},
   321  			Selector: map[string]string{
   322  				"foo": "bar",
   323  			},
   324  		},
   325  	}
   326  
   327  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   328  	if err != nil {
   329  		t.Fatalf("Error creating test service: %v", err)
   330  	}
   331  
   332  	if !serviceHasNodePorts(service) {
   333  		t.Error("expected node ports but found none")
   334  	}
   335  
   336  	clone := service.DeepCopy()
   337  	clone.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
   338  	clone.Spec.Ports[0].NodePort = 0
   339  
   340  	oldData, err := json.Marshal(service)
   341  	if err != nil {
   342  		t.Fatalf("Error marshalling test service: %v", err)
   343  	}
   344  	newData, err := json.Marshal(clone)
   345  	if err != nil {
   346  		t.Fatalf("Error marshalling test service: %v", err)
   347  	}
   348  	patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, corev1.Service{})
   349  	if err != nil {
   350  		t.Fatalf("Error creating patch: %v", err)
   351  	}
   352  
   353  	service, err = client.CoreV1().Services(ns.Name).Patch(context.TODO(), service.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{})
   354  	if err != nil {
   355  		t.Fatalf("Error updating test service: %v", err)
   356  	}
   357  
   358  	if service.Spec.Ports[0].NodePort != 0 {
   359  		t.Error("node ports[0] was expected to be deallocated")
   360  	}
   361  	if service.Spec.Ports[1].NodePort == 0 {
   362  		t.Error("node ports was not expected to be deallocated")
   363  	}
   364  }
   365  
   366  // Test_ServiceLoadBalancerDisableThenEnableAllocatedNodePorts test that switching a Service
   367  // to spec.allocateLoadBalancerNodePorts=true from false, allocate new node ports.
   368  func Test_ServiceLoadBalancerDisableThenEnableAllocatedNodePorts(t *testing.T) {
   369  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   370  	defer server.TearDownFn()
   371  
   372  	client, err := clientset.NewForConfig(server.ClientConfig)
   373  	if err != nil {
   374  		t.Fatalf("Error creating clientset: %v", err)
   375  	}
   376  
   377  	ns := framework.CreateNamespaceOrDie(client, "test-service-reallocate-node-ports", t)
   378  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   379  
   380  	service := &corev1.Service{
   381  		ObjectMeta: metav1.ObjectMeta{
   382  			Name: "test-123",
   383  		},
   384  		Spec: corev1.ServiceSpec{
   385  			Type:                          corev1.ServiceTypeLoadBalancer,
   386  			AllocateLoadBalancerNodePorts: utilpointer.Bool(false),
   387  			Ports: []corev1.ServicePort{{
   388  				Port: int32(80),
   389  			}},
   390  			Selector: map[string]string{
   391  				"foo": "bar",
   392  			},
   393  		},
   394  	}
   395  
   396  	service, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   397  	if err != nil {
   398  		t.Fatalf("Error creating test service: %v", err)
   399  	}
   400  
   401  	if serviceHasNodePorts(service) {
   402  		t.Error("not expected node ports but found one")
   403  	}
   404  
   405  	service.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
   406  	service, err = client.CoreV1().Services(ns.Name).Update(context.TODO(), service, metav1.UpdateOptions{})
   407  	if err != nil {
   408  		t.Fatalf("Error updating test service: %v", err)
   409  	}
   410  
   411  	if !serviceHasNodePorts(service) {
   412  		t.Error("expected node ports but found none")
   413  	}
   414  }
   415  
   416  func serviceHasNodePorts(svc *corev1.Service) bool {
   417  	for _, port := range svc.Spec.Ports {
   418  		if port.NodePort > 0 {
   419  			return true
   420  		}
   421  	}
   422  
   423  	return false
   424  }
   425  
   426  // Test_ServiceLoadBalancerEnableLoadBalancerClass tests that when a LoadBalancer
   427  // type of service has spec.LoadBalancerClass set, cloud provider should not create default load balancer.
   428  func Test_ServiceLoadBalancerEnableLoadBalancerClass(t *testing.T) {
   429  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   430  	defer server.TearDownFn()
   431  
   432  	client, err := clientset.NewForConfig(server.ClientConfig)
   433  	if err != nil {
   434  		t.Fatalf("Error creating clientset: %v", err)
   435  	}
   436  
   437  	ns := framework.CreateNamespaceOrDie(client, "test-service-load-balancer-class", t)
   438  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   439  
   440  	controller, cloud, informer := newServiceController(t, client)
   441  
   442  	ctx, cancel := context.WithCancel(context.Background())
   443  	defer cancel()
   444  	informer.Start(ctx.Done())
   445  	go controller.Run(ctx, 1, controllersmetrics.NewControllerManagerMetrics("loadbalancer-test"))
   446  
   447  	service := &corev1.Service{
   448  		ObjectMeta: metav1.ObjectMeta{
   449  			Name: "test-load-balancer-class",
   450  		},
   451  		Spec: corev1.ServiceSpec{
   452  			Type: corev1.ServiceTypeLoadBalancer,
   453  			Ports: []corev1.ServicePort{{
   454  				Port: int32(80),
   455  			}},
   456  			LoadBalancerClass: utilpointer.String("test.com/test"),
   457  		},
   458  	}
   459  
   460  	_, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})
   461  	if err != nil {
   462  		t.Fatalf("Error creating test service: %v", err)
   463  	}
   464  
   465  	time.Sleep(5 * time.Second) // sleep 5 second to wait for the service controller reconcile
   466  	if len(cloud.Calls) > 0 {
   467  		t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls)
   468  	}
   469  }
   470  
   471  // Test_SetLoadBalancerClassThenUpdateLoadBalancerClass tests that when a LoadBalancer
   472  // type of service has spec.LoadBalancerClass set, it should be immutable as long as the service type
   473  // is still LoadBalancer.
   474  func Test_SetLoadBalancerClassThenUpdateLoadBalancerClass(t *testing.T) {
   475  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   476  	defer server.TearDownFn()
   477  
   478  	client, err := clientset.NewForConfig(server.ClientConfig)
   479  	if err != nil {
   480  		t.Fatalf("Error creating clientset: %v", err)
   481  	}
   482  
   483  	ns := framework.CreateNamespaceOrDie(client, "test-service-immutable-load-balancer-class", t)
   484  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   485  
   486  	controller, cloud, informer := newServiceController(t, client)
   487  
   488  	ctx, cancel := context.WithCancel(context.Background())
   489  	defer cancel()
   490  	informer.Start(ctx.Done())
   491  	go controller.Run(ctx, 1, controllersmetrics.NewControllerManagerMetrics("loadbalancer-test"))
   492  
   493  	service := &corev1.Service{
   494  		ObjectMeta: metav1.ObjectMeta{
   495  			Name: "test-load-balancer-class",
   496  		},
   497  		Spec: corev1.ServiceSpec{
   498  			Type: corev1.ServiceTypeLoadBalancer,
   499  			Ports: []corev1.ServicePort{{
   500  				Port: int32(80),
   501  			}},
   502  			LoadBalancerClass: utilpointer.String("test.com/test"),
   503  		},
   504  	}
   505  
   506  	service, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})
   507  	if err != nil {
   508  		t.Fatalf("Error creating test service: %v", err)
   509  	}
   510  
   511  	service.Spec.LoadBalancerClass = utilpointer.String("test.com/update")
   512  	_, err = client.CoreV1().Services(ns.Name).Update(ctx, service, metav1.UpdateOptions{})
   513  	if err == nil {
   514  		t.Fatal("Error: updating test service load balancer class should throw error, field is immutable")
   515  	}
   516  
   517  	time.Sleep(5 * time.Second) // sleep 5 second to wait for the service controller reconcile
   518  	if len(cloud.Calls) > 0 {
   519  		t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls)
   520  	}
   521  }
   522  
   523  // Test_UpdateLoadBalancerWithLoadBalancerClass tests that when a Load Balancer type of Service that
   524  // is updated from non loadBalancerClass set to loadBalancerClass set, it should be not allowed.
   525  func Test_UpdateLoadBalancerWithLoadBalancerClass(t *testing.T) {
   526  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   527  	defer server.TearDownFn()
   528  
   529  	client, err := clientset.NewForConfig(server.ClientConfig)
   530  	if err != nil {
   531  		t.Fatalf("Error creating clientset: %v", err)
   532  	}
   533  
   534  	ns := framework.CreateNamespaceOrDie(client, "test-service-update-load-balancer-class", t)
   535  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   536  
   537  	controller, cloud, informer := newServiceController(t, client)
   538  
   539  	ctx, cancel := context.WithCancel(context.Background())
   540  	defer cancel()
   541  	informer.Start(ctx.Done())
   542  	go controller.Run(ctx, 1, controllersmetrics.NewControllerManagerMetrics("loadbalancer-test"))
   543  
   544  	service := &corev1.Service{
   545  		ObjectMeta: metav1.ObjectMeta{
   546  			Name: "test-update-load-balancer-class",
   547  		},
   548  		Spec: corev1.ServiceSpec{
   549  			Type: corev1.ServiceTypeLoadBalancer,
   550  			Ports: []corev1.ServicePort{{
   551  				Port: int32(80),
   552  			}},
   553  		},
   554  	}
   555  
   556  	service, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})
   557  	if err != nil {
   558  		t.Fatalf("Error creating test service: %v", err)
   559  	}
   560  
   561  	service.Spec.LoadBalancerClass = utilpointer.String("test.com/test")
   562  	_, err = client.CoreV1().Services(ns.Name).Update(ctx, service, metav1.UpdateOptions{})
   563  	if err == nil {
   564  		t.Fatal("Error: updating test service load balancer class should throw error, field is immutable")
   565  	}
   566  
   567  	time.Sleep(5 * time.Second) // sleep 5 second to wait for the service controller reconcile
   568  	if len(cloud.Calls) == 0 {
   569  		t.Errorf("expected cloud provider calls to create load balancer")
   570  	}
   571  }
   572  
   573  // Test_ServiceLoadBalancerMixedProtocolSetup tests that a LoadBalancer Service with different protocol values
   574  // can be created.
   575  func Test_ServiceLoadBalancerMixedProtocolSetup(t *testing.T) {
   576  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   577  	defer server.TearDownFn()
   578  
   579  	client, err := clientset.NewForConfig(server.ClientConfig)
   580  	if err != nil {
   581  		t.Fatalf("Error creating clientset: %v", err)
   582  	}
   583  
   584  	ns := framework.CreateNamespaceOrDie(client, "test-service-mixed-protocols", t)
   585  	defer framework.DeleteNamespaceOrDie(client, ns, t)
   586  
   587  	controller, cloud, informer := newServiceController(t, client)
   588  
   589  	ctx, cancel := context.WithCancel(context.Background())
   590  	defer cancel()
   591  	informer.Start(ctx.Done())
   592  	go controller.Run(ctx, 1, controllersmetrics.NewControllerManagerMetrics("loadbalancer-test"))
   593  
   594  	service := &corev1.Service{
   595  		ObjectMeta: metav1.ObjectMeta{
   596  			Name: "test-123",
   597  		},
   598  		Spec: corev1.ServiceSpec{
   599  			Type: corev1.ServiceTypeLoadBalancer,
   600  			Ports: []corev1.ServicePort{
   601  				{
   602  					Name:     "tcpport",
   603  					Port:     int32(53),
   604  					Protocol: corev1.ProtocolTCP,
   605  				},
   606  				{
   607  					Name:     "udpport",
   608  					Port:     int32(53),
   609  					Protocol: corev1.ProtocolUDP,
   610  				},
   611  			},
   612  		},
   613  	}
   614  
   615  	_, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), service, metav1.CreateOptions{})
   616  	if err != nil {
   617  		t.Fatalf("Error creating test service: %v", err)
   618  	}
   619  
   620  	time.Sleep(5 * time.Second) // sleep 5 second to wait for the service controller reconcile
   621  	if len(cloud.Calls) == 0 {
   622  		t.Errorf("expected cloud provider calls to create load balancer")
   623  	}
   624  }
   625  
   626  func newServiceController(t *testing.T, client *clientset.Clientset) (*servicecontroller.Controller, *fakecloud.Cloud, informers.SharedInformerFactory) {
   627  	cloud := &fakecloud.Cloud{}
   628  	informerFactory := informers.NewSharedInformerFactory(client, 0)
   629  	serviceInformer := informerFactory.Core().V1().Services()
   630  	nodeInformer := informerFactory.Core().V1().Nodes()
   631  
   632  	controller, err := servicecontroller.New(cloud,
   633  		client,
   634  		serviceInformer,
   635  		nodeInformer,
   636  		"test-cluster",
   637  		utilfeature.DefaultFeatureGate)
   638  	if err != nil {
   639  		t.Fatalf("Error creating service controller: %v", err)
   640  	}
   641  	cloud.ClearCalls() // ignore any cloud calls made in init()
   642  	return controller, cloud, informerFactory
   643  }
   644  
   645  // Test_ServiceLoadBalancerIPMode tests whether the cloud provider has correctly updated the ipMode field.
   646  func Test_ServiceLoadBalancerIPMode(t *testing.T) {
   647  	ipModeVIP := corev1.LoadBalancerIPModeVIP
   648  	testCases := []struct {
   649  		ipModeEnabled  bool
   650  		externalIP     string
   651  		expectedIPMode *corev1.LoadBalancerIPMode
   652  	}{
   653  		{
   654  			ipModeEnabled:  false,
   655  			externalIP:     "1.2.3.4",
   656  			expectedIPMode: nil,
   657  		},
   658  		{
   659  			ipModeEnabled:  true,
   660  			externalIP:     "1.2.3.5",
   661  			expectedIPMode: &ipModeVIP,
   662  		},
   663  	}
   664  
   665  	for _, tc := range testCases {
   666  		t.Run("", func(t *testing.T) {
   667  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled)()
   668  			server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   669  			defer server.TearDownFn()
   670  
   671  			client, err := clientset.NewForConfig(server.ClientConfig)
   672  			if err != nil {
   673  				t.Fatalf("Error creating clientset: %v", err)
   674  			}
   675  
   676  			ns := framework.CreateNamespaceOrDie(client, "test-service-update-load-balancer-ip-mode", t)
   677  			defer framework.DeleteNamespaceOrDie(client, ns, t)
   678  
   679  			controller, cloud, informer := newServiceController(t, client)
   680  			cloud.ExternalIP = net.ParseIPSloppy(tc.externalIP)
   681  
   682  			ctx, cancel := context.WithCancel(context.Background())
   683  			defer cancel()
   684  			informer.Start(ctx.Done())
   685  			go controller.Run(ctx, 1, controllersmetrics.NewControllerManagerMetrics("loadbalancer-test"))
   686  
   687  			service := &corev1.Service{
   688  				ObjectMeta: metav1.ObjectMeta{
   689  					Name: "test-update-load-balancer-ip-mode",
   690  				},
   691  				Spec: corev1.ServiceSpec{
   692  					Type: corev1.ServiceTypeLoadBalancer,
   693  					Ports: []corev1.ServicePort{{
   694  						Port: int32(80),
   695  					}},
   696  				},
   697  			}
   698  
   699  			service, err = client.CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})
   700  			if err != nil {
   701  				t.Fatalf("Error creating test service: %v", err)
   702  			}
   703  
   704  			time.Sleep(5 * time.Second) // sleep 5 second to wait for the service controller reconcile
   705  			service, err = client.CoreV1().Services(ns.Name).Get(ctx, service.Name, metav1.GetOptions{})
   706  			if err != nil {
   707  				t.Fatalf("Error getting test service: %v", err)
   708  			}
   709  
   710  			if len(service.Status.LoadBalancer.Ingress) == 0 {
   711  				t.Fatalf("unexpected load balancer status")
   712  			}
   713  
   714  			gotIngress := service.Status.LoadBalancer.Ingress[0]
   715  			if gotIngress.IP != tc.externalIP || !reflect.DeepEqual(gotIngress.IPMode, tc.expectedIPMode) {
   716  				t.Errorf("unexpected load balancer ingress, got ingress %v, expected IP %v, expected ipMode %v",
   717  					gotIngress, tc.externalIP, tc.expectedIPMode)
   718  			}
   719  		})
   720  	}
   721  }
   722  

View as plain text