k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/service/loadbalancer_test.go (about) 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 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 }