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