k8s.io/kubernetes@v1.29.3/pkg/registry/core/service/strategy_test.go (about) 1 /* 2 Copyright 2014 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 "reflect" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/util/intstr" 27 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 28 "k8s.io/apiserver/pkg/registry/rest" 29 utilfeature "k8s.io/apiserver/pkg/util/feature" 30 featuregatetesting "k8s.io/component-base/featuregate/testing" 31 api "k8s.io/kubernetes/pkg/apis/core" 32 _ "k8s.io/kubernetes/pkg/apis/core/install" 33 "k8s.io/kubernetes/pkg/features" 34 utilpointer "k8s.io/utils/pointer" 35 ) 36 37 func TestCheckGeneratedNameError(t *testing.T) { 38 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ 39 Resource: "foos", 40 }) 41 42 expect := errors.NewNotFound(api.Resource("foos"), "bar") 43 if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{}); err != expect { 44 t.Errorf("NotFoundError should be ignored: %v", err) 45 } 46 47 expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") 48 if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{}); err != expect { 49 t.Errorf("AlreadyExists should be returned when no GenerateName field: %v", err) 50 } 51 52 expect = errors.NewAlreadyExists(api.Resource("foos"), "bar") 53 if err := rest.CheckGeneratedNameError(ctx, Strategy, expect, &api.Service{ObjectMeta: metav1.ObjectMeta{GenerateName: "foo"}}); err == nil || !errors.IsAlreadyExists(err) { 54 t.Errorf("expected try again later error: %v", err) 55 } 56 } 57 58 func makeValidService() *api.Service { 59 preferDual := api.IPFamilyPolicyPreferDualStack 60 clusterInternalTrafficPolicy := api.ServiceInternalTrafficPolicyCluster 61 62 return &api.Service{ 63 ObjectMeta: metav1.ObjectMeta{ 64 Name: "valid", 65 Namespace: "default", 66 Labels: map[string]string{}, 67 Annotations: map[string]string{}, 68 ResourceVersion: "1", 69 }, 70 Spec: api.ServiceSpec{ 71 Selector: map[string]string{"key": "val"}, 72 SessionAffinity: "None", 73 Type: api.ServiceTypeClusterIP, 74 Ports: []api.ServicePort{ 75 makeValidServicePort("p", "TCP", 8675), 76 makeValidServicePort("q", "TCP", 309), 77 }, 78 ClusterIP: "1.2.3.4", 79 ClusterIPs: []string{"1.2.3.4", "5:6:7::8"}, 80 IPFamilyPolicy: &preferDual, 81 IPFamilies: []api.IPFamily{"IPv4", "IPv6"}, 82 InternalTrafficPolicy: &clusterInternalTrafficPolicy, 83 }, 84 } 85 } 86 87 func makeValidServicePort(name string, proto api.Protocol, port int32) api.ServicePort { 88 return api.ServicePort{ 89 Name: name, 90 Protocol: proto, 91 Port: port, 92 TargetPort: intstr.FromInt32(port), 93 } 94 } 95 96 func makeValidServiceCustom(tweaks ...func(svc *api.Service)) *api.Service { 97 svc := makeValidService() 98 for _, fn := range tweaks { 99 fn(svc) 100 } 101 return svc 102 } 103 104 func TestServiceStatusStrategy(t *testing.T) { 105 ctx := genericapirequest.NewDefaultContext() 106 if !StatusStrategy.NamespaceScoped() { 107 t.Errorf("Service must be namespace scoped") 108 } 109 oldService := makeValidService() 110 oldService.Spec.Type = api.ServiceTypeLoadBalancer 111 oldService.ResourceVersion = "4" 112 oldService.Spec.SessionAffinity = "None" 113 newService := oldService.DeepCopy() 114 newService.Spec.SessionAffinity = "ClientIP" 115 newService.Status = api.ServiceStatus{ 116 LoadBalancer: api.LoadBalancerStatus{ 117 Ingress: []api.LoadBalancerIngress{ 118 {IP: "127.0.0.2"}, 119 }, 120 }, 121 } 122 StatusStrategy.PrepareForUpdate(ctx, newService, oldService) 123 if newService.Status.LoadBalancer.Ingress[0].IP != "127.0.0.2" { 124 t.Errorf("Service status updates should allow change of status fields") 125 } 126 if newService.Spec.SessionAffinity != "None" { 127 t.Errorf("PrepareForUpdate should have preserved old spec") 128 } 129 errs := StatusStrategy.ValidateUpdate(ctx, newService, oldService) 130 if len(errs) != 0 { 131 t.Errorf("Unexpected error %v", errs) 132 } 133 } 134 135 func makeServiceWithConditions(conditions []metav1.Condition) *api.Service { 136 return &api.Service{ 137 Status: api.ServiceStatus{ 138 Conditions: conditions, 139 }, 140 } 141 } 142 143 func makeServiceWithPorts(ports []api.PortStatus) *api.Service { 144 return &api.Service{ 145 Status: api.ServiceStatus{ 146 LoadBalancer: api.LoadBalancerStatus{ 147 Ingress: []api.LoadBalancerIngress{ 148 { 149 Ports: ports, 150 }, 151 }, 152 }, 153 }, 154 } 155 } 156 157 func TestDropDisabledField(t *testing.T) { 158 testCases := []struct { 159 name string 160 svc *api.Service 161 oldSvc *api.Service 162 compareSvc *api.Service 163 }{ 164 /* svc.Status.Conditions */ 165 { 166 name: "mixed protocol enabled, field not used in old, not used in new", 167 svc: makeServiceWithConditions(nil), 168 oldSvc: makeServiceWithConditions(nil), 169 compareSvc: makeServiceWithConditions(nil), 170 }, 171 { 172 name: "mixed protocol enabled, field used in old and in new", 173 svc: makeServiceWithConditions([]metav1.Condition{}), 174 oldSvc: makeServiceWithConditions([]metav1.Condition{}), 175 compareSvc: makeServiceWithConditions([]metav1.Condition{}), 176 }, 177 { 178 name: "mixed protocol enabled, field not used in old, used in new", 179 svc: makeServiceWithConditions([]metav1.Condition{}), 180 oldSvc: makeServiceWithConditions(nil), 181 compareSvc: makeServiceWithConditions([]metav1.Condition{}), 182 }, 183 { 184 name: "mixed protocol enabled, field used in old, not used in new", 185 svc: makeServiceWithConditions(nil), 186 oldSvc: makeServiceWithConditions([]metav1.Condition{}), 187 compareSvc: makeServiceWithConditions(nil), 188 }, 189 /* svc.Status.LoadBalancer.Ingress.Ports */ 190 { 191 name: "mixed protocol enabled, field not used in old, not used in new", 192 svc: makeServiceWithPorts(nil), 193 oldSvc: makeServiceWithPorts(nil), 194 compareSvc: makeServiceWithPorts(nil), 195 }, 196 { 197 name: "mixed protocol enabled, field used in old and in new", 198 svc: makeServiceWithPorts([]api.PortStatus{}), 199 oldSvc: makeServiceWithPorts([]api.PortStatus{}), 200 compareSvc: makeServiceWithPorts([]api.PortStatus{}), 201 }, 202 { 203 name: "mixed protocol enabled, field not used in old, used in new", 204 svc: makeServiceWithPorts([]api.PortStatus{}), 205 oldSvc: makeServiceWithPorts(nil), 206 compareSvc: makeServiceWithPorts([]api.PortStatus{}), 207 }, 208 { 209 name: "mixed protocol enabled, field used in old, not used in new", 210 svc: makeServiceWithPorts(nil), 211 oldSvc: makeServiceWithPorts([]api.PortStatus{}), 212 compareSvc: makeServiceWithPorts(nil), 213 }, 214 /* add more tests for other dropped fields as needed */ 215 } 216 for _, tc := range testCases { 217 func() { 218 old := tc.oldSvc.DeepCopy() 219 220 // to test against user using IPFamily not set on cluster 221 dropServiceDisabledFields(tc.svc, tc.oldSvc) 222 223 // old node should never be changed 224 if !reflect.DeepEqual(tc.oldSvc, old) { 225 t.Errorf("%v: old svc changed: %v", tc.name, cmp.Diff(tc.oldSvc, old)) 226 } 227 228 if !reflect.DeepEqual(tc.svc, tc.compareSvc) { 229 t.Errorf("%v: unexpected svc spec: %v", tc.name, cmp.Diff(tc.svc, tc.compareSvc)) 230 } 231 }() 232 } 233 234 } 235 236 func TestDropServiceStatusDisabledFields(t *testing.T) { 237 ipModeVIP := api.LoadBalancerIPModeVIP 238 ipModeProxy := api.LoadBalancerIPModeProxy 239 240 testCases := []struct { 241 name string 242 ipModeEnabled bool 243 svc *api.Service 244 oldSvc *api.Service 245 compareSvc *api.Service 246 }{ 247 /*LoadBalancerIPMode disabled*/ 248 { 249 name: "LoadBalancerIPMode disabled, ipMode not used in old, not used in new", 250 ipModeEnabled: false, 251 svc: makeValidServiceCustom(func(svc *api.Service) { 252 svc.Spec.Type = api.ServiceTypeLoadBalancer 253 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 254 Ingress: []api.LoadBalancerIngress{{ 255 IP: "1.2.3.4", 256 }}, 257 } 258 }), 259 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 260 svc.Spec.Type = api.ServiceTypeLoadBalancer 261 svc.Status.LoadBalancer = api.LoadBalancerStatus{} 262 }), 263 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 264 svc.Spec.Type = api.ServiceTypeLoadBalancer 265 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 266 Ingress: []api.LoadBalancerIngress{{ 267 IP: "1.2.3.4", 268 }}, 269 } 270 }), 271 }, { 272 name: "LoadBalancerIPMode disabled, ipMode used in old and in new", 273 ipModeEnabled: false, 274 svc: makeValidServiceCustom(func(svc *api.Service) { 275 svc.Spec.Type = api.ServiceTypeLoadBalancer 276 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 277 Ingress: []api.LoadBalancerIngress{{ 278 IP: "1.2.3.4", 279 IPMode: &ipModeProxy, 280 }}, 281 } 282 }), 283 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 284 svc.Spec.Type = api.ServiceTypeLoadBalancer 285 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 286 Ingress: []api.LoadBalancerIngress{{ 287 IP: "1.2.3.4", 288 IPMode: &ipModeVIP, 289 }}, 290 } 291 }), 292 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 293 svc.Spec.Type = api.ServiceTypeLoadBalancer 294 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 295 Ingress: []api.LoadBalancerIngress{{ 296 IP: "1.2.3.4", 297 IPMode: &ipModeProxy, 298 }}, 299 } 300 }), 301 }, { 302 name: "LoadBalancerIPMode disabled, ipMode not used in old, used in new", 303 ipModeEnabled: false, 304 svc: makeValidServiceCustom(func(svc *api.Service) { 305 svc.Spec.Type = api.ServiceTypeLoadBalancer 306 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 307 Ingress: []api.LoadBalancerIngress{{ 308 IP: "1.2.3.4", 309 IPMode: &ipModeVIP, 310 }}, 311 } 312 }), 313 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 314 svc.Spec.Type = api.ServiceTypeLoadBalancer 315 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 316 Ingress: []api.LoadBalancerIngress{{ 317 IP: "1.2.3.4", 318 }}, 319 } 320 }), 321 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 322 svc.Spec.Type = api.ServiceTypeLoadBalancer 323 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 324 Ingress: []api.LoadBalancerIngress{{ 325 IP: "1.2.3.4", 326 }}, 327 } 328 }), 329 }, { 330 name: "LoadBalancerIPMode disabled, ipMode used in old, not used in new", 331 ipModeEnabled: false, 332 svc: makeValidServiceCustom(func(svc *api.Service) { 333 svc.Spec.Type = api.ServiceTypeLoadBalancer 334 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 335 Ingress: []api.LoadBalancerIngress{{ 336 IP: "1.2.3.4", 337 }}, 338 } 339 }), 340 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 341 svc.Spec.Type = api.ServiceTypeLoadBalancer 342 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 343 Ingress: []api.LoadBalancerIngress{{ 344 IP: "1.2.3.4", 345 IPMode: &ipModeProxy, 346 }}, 347 } 348 }), 349 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 350 svc.Spec.Type = api.ServiceTypeLoadBalancer 351 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 352 Ingress: []api.LoadBalancerIngress{{ 353 IP: "1.2.3.4", 354 }}, 355 } 356 }), 357 }, 358 /*LoadBalancerIPMode enabled*/ 359 { 360 name: "LoadBalancerIPMode enabled, ipMode not used in old, not used in new", 361 ipModeEnabled: true, 362 svc: makeValidServiceCustom(func(svc *api.Service) { 363 svc.Spec.Type = api.ServiceTypeLoadBalancer 364 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 365 Ingress: []api.LoadBalancerIngress{{ 366 IP: "1.2.3.4", 367 }}, 368 } 369 }), 370 oldSvc: nil, 371 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 372 svc.Spec.Type = api.ServiceTypeLoadBalancer 373 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 374 Ingress: []api.LoadBalancerIngress{{ 375 IP: "1.2.3.4", 376 }}, 377 } 378 }), 379 }, { 380 name: "LoadBalancerIPMode enabled, ipMode used in old and in new", 381 ipModeEnabled: true, 382 svc: makeValidServiceCustom(func(svc *api.Service) { 383 svc.Spec.Type = api.ServiceTypeLoadBalancer 384 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 385 Ingress: []api.LoadBalancerIngress{{ 386 IP: "1.2.3.4", 387 IPMode: &ipModeProxy, 388 }}, 389 } 390 }), 391 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 392 svc.Spec.Type = api.ServiceTypeLoadBalancer 393 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 394 Ingress: []api.LoadBalancerIngress{{ 395 IP: "1.2.3.4", 396 IPMode: &ipModeVIP, 397 }}, 398 } 399 }), 400 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 401 svc.Spec.Type = api.ServiceTypeLoadBalancer 402 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 403 Ingress: []api.LoadBalancerIngress{{ 404 IP: "1.2.3.4", 405 IPMode: &ipModeProxy, 406 }}, 407 } 408 }), 409 }, { 410 name: "LoadBalancerIPMode enabled, ipMode not used in old, used in new", 411 ipModeEnabled: true, 412 svc: makeValidServiceCustom(func(svc *api.Service) { 413 svc.Spec.Type = api.ServiceTypeLoadBalancer 414 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 415 Ingress: []api.LoadBalancerIngress{{ 416 IP: "1.2.3.4", 417 IPMode: &ipModeVIP, 418 }}, 419 } 420 }), 421 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 422 svc.Spec.Type = api.ServiceTypeLoadBalancer 423 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 424 Ingress: []api.LoadBalancerIngress{{ 425 IP: "1.2.3.4", 426 }}, 427 } 428 }), 429 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 430 svc.Spec.Type = api.ServiceTypeLoadBalancer 431 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 432 Ingress: []api.LoadBalancerIngress{{ 433 IP: "1.2.3.4", 434 IPMode: &ipModeVIP, 435 }}, 436 } 437 }), 438 }, { 439 name: "LoadBalancerIPMode enabled, ipMode used in old, not used in new", 440 ipModeEnabled: true, 441 svc: makeValidServiceCustom(func(svc *api.Service) { 442 svc.Spec.Type = api.ServiceTypeLoadBalancer 443 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 444 Ingress: []api.LoadBalancerIngress{{ 445 IP: "1.2.3.4", 446 }}, 447 } 448 }), 449 oldSvc: makeValidServiceCustom(func(svc *api.Service) { 450 svc.Spec.Type = api.ServiceTypeLoadBalancer 451 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 452 Ingress: []api.LoadBalancerIngress{{ 453 IP: "1.2.3.4", 454 IPMode: &ipModeProxy, 455 }}, 456 } 457 }), 458 compareSvc: makeValidServiceCustom(func(svc *api.Service) { 459 svc.Spec.Type = api.ServiceTypeLoadBalancer 460 svc.Status.LoadBalancer = api.LoadBalancerStatus{ 461 Ingress: []api.LoadBalancerIngress{{ 462 IP: "1.2.3.4", 463 }}, 464 } 465 }), 466 }, 467 } 468 469 for _, tc := range testCases { 470 t.Run(tc.name, func(t *testing.T) { 471 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled)() 472 dropServiceStatusDisabledFields(tc.svc, tc.oldSvc) 473 474 if !reflect.DeepEqual(tc.svc, tc.compareSvc) { 475 t.Errorf("%v: unexpected svc spec: %v", tc.name, cmp.Diff(tc.svc, tc.compareSvc)) 476 } 477 }) 478 } 479 } 480 481 func TestDropTypeDependentFields(t *testing.T) { 482 // Tweaks used below. 483 setTypeExternalName := func(svc *api.Service) { 484 svc.Spec.Type = api.ServiceTypeExternalName 485 } 486 setTypeNodePort := func(svc *api.Service) { 487 svc.Spec.Type = api.ServiceTypeNodePort 488 } 489 setTypeClusterIP := func(svc *api.Service) { 490 svc.Spec.Type = api.ServiceTypeClusterIP 491 } 492 setTypeLoadBalancer := func(svc *api.Service) { 493 svc.Spec.Type = api.ServiceTypeLoadBalancer 494 } 495 clearClusterIPs := func(svc *api.Service) { 496 svc.Spec.ClusterIP = "" 497 svc.Spec.ClusterIPs = nil 498 } 499 changeClusterIPs := func(svc *api.Service) { 500 svc.Spec.ClusterIP += "0" 501 svc.Spec.ClusterIPs[0] += "0" 502 } 503 setNodePorts := func(svc *api.Service) { 504 for i := range svc.Spec.Ports { 505 svc.Spec.Ports[i].NodePort = int32(30000 + i) 506 } 507 } 508 changeNodePorts := func(svc *api.Service) { 509 for i := range svc.Spec.Ports { 510 svc.Spec.Ports[i].NodePort += 100 511 } 512 } 513 setExternalIPs := func(svc *api.Service) { 514 svc.Spec.ExternalIPs = []string{"1.1.1.1"} 515 } 516 clearExternalIPs := func(svc *api.Service) { 517 svc.Spec.ExternalIPs = nil 518 } 519 setExternalTrafficPolicyCluster := func(svc *api.Service) { 520 svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyCluster 521 } 522 clearExternalTrafficPolicy := func(svc *api.Service) { 523 svc.Spec.ExternalTrafficPolicy = "" 524 } 525 clearIPFamilies := func(svc *api.Service) { 526 svc.Spec.IPFamilies = nil 527 } 528 changeIPFamilies := func(svc *api.Service) { 529 svc.Spec.IPFamilies[0] = svc.Spec.IPFamilies[1] 530 } 531 clearIPFamilyPolicy := func(svc *api.Service) { 532 svc.Spec.IPFamilyPolicy = nil 533 } 534 changeIPFamilyPolicy := func(svc *api.Service) { 535 single := api.IPFamilyPolicySingleStack 536 svc.Spec.IPFamilyPolicy = &single 537 } 538 addPort := func(svc *api.Service) { 539 svc.Spec.Ports = append(svc.Spec.Ports, makeValidServicePort("new", "TCP", 0)) 540 } 541 delPort := func(svc *api.Service) { 542 svc.Spec.Ports = svc.Spec.Ports[0 : len(svc.Spec.Ports)-1] 543 } 544 changePort := func(svc *api.Service) { 545 svc.Spec.Ports[0].Port += 100 546 svc.Spec.Ports[0].Protocol = "UDP" 547 } 548 setHCNodePort := func(svc *api.Service) { 549 svc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicyLocal 550 svc.Spec.HealthCheckNodePort = int32(32000) 551 } 552 changeHCNodePort := func(svc *api.Service) { 553 svc.Spec.HealthCheckNodePort += 100 554 } 555 patches := func(fns ...func(svc *api.Service)) func(svc *api.Service) { 556 return func(svc *api.Service) { 557 for _, fn := range fns { 558 fn(svc) 559 } 560 } 561 } 562 setAllocateLoadBalancerNodePortsTrue := func(svc *api.Service) { 563 svc.Spec.AllocateLoadBalancerNodePorts = utilpointer.BoolPtr(true) 564 } 565 setAllocateLoadBalancerNodePortsFalse := func(svc *api.Service) { 566 svc.Spec.AllocateLoadBalancerNodePorts = utilpointer.BoolPtr(false) 567 } 568 clearAllocateLoadBalancerNodePorts := func(svc *api.Service) { 569 svc.Spec.AllocateLoadBalancerNodePorts = nil 570 } 571 setLoadBalancerClass := func(svc *api.Service) { 572 svc.Spec.LoadBalancerClass = utilpointer.String("test-load-balancer-class") 573 } 574 clearLoadBalancerClass := func(svc *api.Service) { 575 svc.Spec.LoadBalancerClass = nil 576 } 577 changeLoadBalancerClass := func(svc *api.Service) { 578 svc.Spec.LoadBalancerClass = utilpointer.String("test-load-balancer-class-changed") 579 } 580 581 testCases := []struct { 582 name string 583 svc *api.Service 584 patch func(svc *api.Service) 585 expect *api.Service 586 }{ 587 { // clusterIP cases 588 name: "don't clear clusterIP et al", 589 svc: makeValidService(), 590 patch: nil, 591 expect: makeValidService(), 592 }, { 593 name: "clear clusterIP et al", 594 svc: makeValidService(), 595 patch: setTypeExternalName, 596 expect: makeValidServiceCustom(setTypeExternalName, clearClusterIPs, clearIPFamilies, clearIPFamilyPolicy), 597 }, { 598 name: "don't clear changed clusterIP", 599 svc: makeValidService(), 600 patch: patches(setTypeExternalName, changeClusterIPs), 601 expect: makeValidServiceCustom(setTypeExternalName, changeClusterIPs, clearIPFamilies, clearIPFamilyPolicy), 602 }, { 603 name: "don't clear changed ipFamilies", 604 svc: makeValidService(), 605 patch: patches(setTypeExternalName, changeIPFamilies), 606 expect: makeValidServiceCustom(setTypeExternalName, clearClusterIPs, changeIPFamilies, clearIPFamilyPolicy), 607 }, { 608 name: "don't clear changed ipFamilyPolicy", 609 svc: makeValidService(), 610 patch: patches(setTypeExternalName, changeIPFamilyPolicy), 611 expect: makeValidServiceCustom(setTypeExternalName, clearClusterIPs, clearIPFamilies, changeIPFamilyPolicy), 612 }, { // nodePort cases 613 name: "don't clear nodePorts for type=NodePort", 614 svc: makeValidServiceCustom(setTypeNodePort, setNodePorts), 615 patch: nil, 616 expect: makeValidServiceCustom(setTypeNodePort, setNodePorts), 617 }, { 618 name: "don't clear nodePorts for type=LoadBalancer", 619 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 620 patch: nil, 621 expect: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 622 }, { 623 name: "clear nodePorts", 624 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 625 patch: setTypeClusterIP, 626 expect: makeValidService(), 627 }, { 628 name: "don't clear changed nodePorts", 629 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 630 patch: patches(setTypeClusterIP, changeNodePorts), 631 expect: makeValidServiceCustom(setNodePorts, changeNodePorts), 632 }, { 633 name: "clear nodePorts when adding a port", 634 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 635 patch: patches(setTypeClusterIP, addPort), 636 expect: makeValidServiceCustom(addPort), 637 }, { 638 name: "don't clear nodePorts when adding a port with NodePort", 639 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 640 patch: patches(setTypeClusterIP, addPort, setNodePorts), 641 expect: makeValidServiceCustom(addPort, setNodePorts), 642 }, { 643 name: "clear nodePorts when removing a port", 644 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 645 patch: patches(setTypeClusterIP, delPort), 646 expect: makeValidServiceCustom(delPort), 647 }, { 648 name: "clear nodePorts when changing a port", 649 svc: makeValidServiceCustom(setTypeLoadBalancer, setNodePorts), 650 patch: patches(setTypeClusterIP, changePort), 651 expect: makeValidServiceCustom(changePort), 652 }, { // healthCheckNodePort cases 653 name: "don't clear healthCheckNodePort for type=LoadBalancer", 654 svc: makeValidServiceCustom(setTypeLoadBalancer, setHCNodePort), 655 patch: nil, 656 expect: makeValidServiceCustom(setTypeLoadBalancer, setHCNodePort), 657 }, { 658 name: "clear healthCheckNodePort", 659 svc: makeValidServiceCustom(setTypeLoadBalancer, setHCNodePort), 660 patch: setTypeClusterIP, 661 expect: makeValidService(), 662 }, { 663 name: "don't clear changed healthCheckNodePort", 664 svc: makeValidServiceCustom(setTypeLoadBalancer, setHCNodePort), 665 patch: patches(setTypeClusterIP, changeHCNodePort), 666 expect: makeValidServiceCustom(setHCNodePort, changeHCNodePort, clearExternalTrafficPolicy), 667 }, { // allocatedLoadBalancerNodePorts cases 668 name: "clear allocatedLoadBalancerNodePorts true -> true", 669 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsTrue), 670 patch: setTypeNodePort, 671 expect: makeValidServiceCustom(setTypeNodePort), 672 }, { 673 name: "clear allocatedLoadBalancerNodePorts false -> false", 674 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsFalse), 675 patch: setTypeNodePort, 676 expect: makeValidServiceCustom(setTypeNodePort), 677 }, { 678 name: "set allocatedLoadBalancerNodePorts nil -> true", 679 svc: makeValidServiceCustom(setTypeLoadBalancer), 680 patch: patches(setTypeNodePort, setAllocateLoadBalancerNodePortsTrue), 681 expect: makeValidServiceCustom(setTypeNodePort, setAllocateLoadBalancerNodePortsTrue), 682 }, { 683 name: "set allocatedLoadBalancerNodePorts nil -> false", 684 svc: makeValidServiceCustom(setTypeLoadBalancer), 685 patch: patches(setTypeNodePort, setAllocateLoadBalancerNodePortsFalse), 686 expect: makeValidServiceCustom(setTypeNodePort, setAllocateLoadBalancerNodePortsFalse), 687 }, { 688 name: "set allocatedLoadBalancerNodePorts true -> nil", 689 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsTrue), 690 patch: patches(setTypeNodePort, clearAllocateLoadBalancerNodePorts), 691 expect: makeValidServiceCustom(setTypeNodePort), 692 }, { 693 name: "set allocatedLoadBalancerNodePorts false -> nil", 694 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsFalse), 695 patch: patches(setTypeNodePort, clearAllocateLoadBalancerNodePorts), 696 expect: makeValidServiceCustom(setTypeNodePort), 697 }, { 698 name: "set allocatedLoadBalancerNodePorts true -> false", 699 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsTrue), 700 patch: patches(setTypeNodePort, setAllocateLoadBalancerNodePortsFalse), 701 expect: makeValidServiceCustom(setTypeNodePort, setAllocateLoadBalancerNodePortsFalse), 702 }, { 703 name: "set allocatedLoadBalancerNodePorts false -> true", 704 svc: makeValidServiceCustom(setTypeLoadBalancer, setAllocateLoadBalancerNodePortsFalse), 705 patch: patches(setTypeNodePort, setAllocateLoadBalancerNodePortsTrue), 706 expect: makeValidServiceCustom(setTypeNodePort, setAllocateLoadBalancerNodePortsTrue), 707 }, { // loadBalancerClass cases 708 name: "clear loadBalancerClass when set Service type LoadBalancer -> non LoadBalancer", 709 svc: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 710 patch: setTypeClusterIP, 711 expect: makeValidServiceCustom(setTypeClusterIP, clearLoadBalancerClass), 712 }, { 713 name: "update loadBalancerClass load balancer class name", 714 svc: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 715 patch: changeLoadBalancerClass, 716 expect: makeValidServiceCustom(setTypeLoadBalancer, changeLoadBalancerClass), 717 }, { 718 name: "clear load balancer class name", 719 svc: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 720 patch: clearLoadBalancerClass, 721 expect: makeValidServiceCustom(setTypeLoadBalancer, clearLoadBalancerClass), 722 }, { 723 name: "change service type and load balancer class", 724 svc: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 725 patch: patches(setTypeClusterIP, changeLoadBalancerClass), 726 expect: makeValidServiceCustom(setTypeClusterIP, changeLoadBalancerClass), 727 }, { 728 name: "change service type to load balancer and set load balancer class", 729 svc: makeValidServiceCustom(setTypeClusterIP), 730 patch: patches(setTypeLoadBalancer, setLoadBalancerClass), 731 expect: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 732 }, { 733 name: "don't clear load balancer class for Type=LoadBalancer", 734 svc: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 735 patch: nil, 736 expect: makeValidServiceCustom(setTypeLoadBalancer, setLoadBalancerClass), 737 }, { 738 name: "clear externalTrafficPolicy when removing externalIPs for Type=ClusterIP", 739 svc: makeValidServiceCustom(setTypeClusterIP, setExternalIPs, setExternalTrafficPolicyCluster), 740 patch: patches(clearExternalIPs), 741 expect: makeValidServiceCustom(setTypeClusterIP, clearExternalTrafficPolicy), 742 }} 743 744 for _, tc := range testCases { 745 t.Run(tc.name, func(t *testing.T) { 746 result := tc.svc.DeepCopy() 747 if tc.patch != nil { 748 tc.patch(result) 749 } 750 dropTypeDependentFields(result, tc.svc) 751 if result.Spec.ClusterIP != tc.expect.Spec.ClusterIP { 752 t.Errorf("expected clusterIP %q, got %q", tc.expect.Spec.ClusterIP, result.Spec.ClusterIP) 753 } 754 if !reflect.DeepEqual(result.Spec.ClusterIPs, tc.expect.Spec.ClusterIPs) { 755 t.Errorf("expected clusterIPs %q, got %q", tc.expect.Spec.ClusterIP, result.Spec.ClusterIP) 756 } 757 if !reflect.DeepEqual(result.Spec.IPFamilies, tc.expect.Spec.IPFamilies) { 758 t.Errorf("expected ipFamilies %q, got %q", tc.expect.Spec.IPFamilies, result.Spec.IPFamilies) 759 } 760 if !reflect.DeepEqual(result.Spec.IPFamilyPolicy, tc.expect.Spec.IPFamilyPolicy) { 761 t.Errorf("expected ipFamilyPolicy %q, got %q", getIPFamilyPolicy(tc.expect), getIPFamilyPolicy(result)) 762 } 763 for i := range result.Spec.Ports { 764 resultPort := result.Spec.Ports[i].NodePort 765 expectPort := tc.expect.Spec.Ports[i].NodePort 766 if resultPort != expectPort { 767 t.Errorf("failed %q: expected Ports[%d].NodePort %d, got %d", tc.name, i, expectPort, resultPort) 768 } 769 } 770 if result.Spec.HealthCheckNodePort != tc.expect.Spec.HealthCheckNodePort { 771 t.Errorf("failed %q: expected healthCheckNodePort %d, got %d", tc.name, tc.expect.Spec.HealthCheckNodePort, result.Spec.HealthCheckNodePort) 772 } 773 if !reflect.DeepEqual(result.Spec.AllocateLoadBalancerNodePorts, tc.expect.Spec.AllocateLoadBalancerNodePorts) { 774 t.Errorf("failed %q: expected AllocateLoadBalancerNodePorts %v, got %v", tc.name, tc.expect.Spec.AllocateLoadBalancerNodePorts, result.Spec.AllocateLoadBalancerNodePorts) 775 } 776 if !reflect.DeepEqual(result.Spec.LoadBalancerClass, tc.expect.Spec.LoadBalancerClass) { 777 t.Errorf("failed %q: expected LoadBalancerClass %v, got %v", tc.name, tc.expect.Spec.LoadBalancerClass, result.Spec.LoadBalancerClass) 778 } 779 if !reflect.DeepEqual(result.Spec.ExternalTrafficPolicy, tc.expect.Spec.ExternalTrafficPolicy) { 780 t.Errorf("failed %q: expected ExternalTrafficPolicy %v, got %v", tc.name, tc.expect.Spec.ExternalTrafficPolicy, result.Spec.ExternalTrafficPolicy) 781 } 782 }) 783 } 784 }