1
16
17 package endpointslice
18
19 import (
20 "context"
21 "testing"
22
23 corev1 "k8s.io/api/core/v1"
24 apiequality "k8s.io/apimachinery/pkg/api/equality"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/util/sets"
27 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
28 utilfeature "k8s.io/apiserver/pkg/util/feature"
29 featuregatetesting "k8s.io/component-base/featuregate/testing"
30 "k8s.io/kubernetes/pkg/apis/discovery"
31 "k8s.io/kubernetes/pkg/features"
32 utilpointer "k8s.io/utils/pointer"
33 )
34
35 func Test_dropDisabledFieldsOnCreate(t *testing.T) {
36 testcases := []struct {
37 name string
38 hintsGateEnabled bool
39 eps *discovery.EndpointSlice
40 expectedEPS *discovery.EndpointSlice
41 }{
42 {
43 name: "node name gate enabled, field should be allowed",
44 eps: &discovery.EndpointSlice{
45 Endpoints: []discovery.Endpoint{
46 {
47 NodeName: utilpointer.String("node-1"),
48 },
49 {
50 NodeName: utilpointer.String("node-2"),
51 },
52 },
53 },
54 expectedEPS: &discovery.EndpointSlice{
55 Endpoints: []discovery.Endpoint{
56 {
57 NodeName: utilpointer.String("node-1"),
58 },
59 {
60 NodeName: utilpointer.String("node-2"),
61 },
62 },
63 },
64 },
65 }
66
67 for _, testcase := range testcases {
68 t.Run(testcase.name, func(t *testing.T) {
69 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, testcase.hintsGateEnabled)()
70
71 dropDisabledFieldsOnCreate(testcase.eps)
72 if !apiequality.Semantic.DeepEqual(testcase.eps, testcase.expectedEPS) {
73 t.Logf("actual endpointslice: %v", testcase.eps)
74 t.Logf("expected endpointslice: %v", testcase.expectedEPS)
75 t.Errorf("unexpected EndpointSlice on create API strategy")
76 }
77 })
78 }
79 }
80
81 func Test_dropDisabledFieldsOnUpdate(t *testing.T) {
82 testcases := []struct {
83 name string
84 hintsGateEnabled bool
85 oldEPS *discovery.EndpointSlice
86 newEPS *discovery.EndpointSlice
87 expectedEPS *discovery.EndpointSlice
88 }{
89 {
90 name: "node name gate enabled, set on new EPS",
91 oldEPS: &discovery.EndpointSlice{
92 Endpoints: []discovery.Endpoint{
93 {
94 NodeName: nil,
95 },
96 {
97 NodeName: nil,
98 },
99 },
100 },
101 newEPS: &discovery.EndpointSlice{
102 Endpoints: []discovery.Endpoint{
103 {
104 NodeName: utilpointer.String("node-1"),
105 },
106 {
107 NodeName: utilpointer.String("node-2"),
108 },
109 },
110 },
111 expectedEPS: &discovery.EndpointSlice{
112 Endpoints: []discovery.Endpoint{
113 {
114 NodeName: utilpointer.String("node-1"),
115 },
116 {
117 NodeName: utilpointer.String("node-2"),
118 },
119 },
120 },
121 },
122 {
123 name: "node name gate disabled, set on old and updated EPS",
124 oldEPS: &discovery.EndpointSlice{
125 Endpoints: []discovery.Endpoint{
126 {
127 NodeName: utilpointer.String("node-1-old"),
128 },
129 {
130 NodeName: utilpointer.String("node-2-old"),
131 },
132 },
133 },
134 newEPS: &discovery.EndpointSlice{
135 Endpoints: []discovery.Endpoint{
136 {
137 NodeName: utilpointer.String("node-1"),
138 },
139 {
140 NodeName: utilpointer.String("node-2"),
141 },
142 },
143 },
144 expectedEPS: &discovery.EndpointSlice{
145 Endpoints: []discovery.Endpoint{
146 {
147 NodeName: utilpointer.String("node-1"),
148 },
149 {
150 NodeName: utilpointer.String("node-2"),
151 },
152 },
153 },
154 },
155 {
156 name: "hints gate enabled, set on new EPS",
157 hintsGateEnabled: true,
158 oldEPS: &discovery.EndpointSlice{
159 Endpoints: []discovery.Endpoint{
160 {
161 Hints: nil,
162 },
163 {
164 Hints: nil,
165 },
166 },
167 },
168 newEPS: &discovery.EndpointSlice{
169 Endpoints: []discovery.Endpoint{
170 {
171 Hints: &discovery.EndpointHints{
172 ForZones: []discovery.ForZone{{Name: "zone-a"}},
173 },
174 },
175 {
176 Hints: &discovery.EndpointHints{
177 ForZones: []discovery.ForZone{{Name: "zone-b"}},
178 },
179 },
180 },
181 },
182 expectedEPS: &discovery.EndpointSlice{
183 Endpoints: []discovery.Endpoint{
184 {
185 Hints: &discovery.EndpointHints{
186 ForZones: []discovery.ForZone{{Name: "zone-a"}},
187 },
188 },
189 {
190 Hints: &discovery.EndpointHints{
191 ForZones: []discovery.ForZone{{Name: "zone-b"}},
192 },
193 },
194 },
195 },
196 },
197 {
198 name: "hints gate disabled, set on new EPS",
199 hintsGateEnabled: false,
200 oldEPS: &discovery.EndpointSlice{
201 Endpoints: []discovery.Endpoint{
202 {
203 Hints: nil,
204 },
205 {
206 Hints: nil,
207 },
208 },
209 },
210 newEPS: &discovery.EndpointSlice{
211 Endpoints: []discovery.Endpoint{
212 {
213 Hints: &discovery.EndpointHints{
214 ForZones: []discovery.ForZone{{Name: "zone-a"}},
215 },
216 },
217 {
218 Hints: &discovery.EndpointHints{
219 ForZones: []discovery.ForZone{{Name: "zone-b"}},
220 },
221 },
222 },
223 },
224 expectedEPS: &discovery.EndpointSlice{
225 Endpoints: []discovery.Endpoint{
226 {
227 Hints: nil,
228 },
229 {
230 Hints: nil,
231 },
232 },
233 },
234 },
235 {
236 name: "hints gate disabled, set on new and old EPS",
237 hintsGateEnabled: false,
238 oldEPS: &discovery.EndpointSlice{
239 Endpoints: []discovery.Endpoint{
240 {
241 Hints: &discovery.EndpointHints{
242 ForZones: []discovery.ForZone{{Name: "zone-a-old"}},
243 },
244 },
245 {
246 Hints: &discovery.EndpointHints{
247 ForZones: []discovery.ForZone{{Name: "zone-b-old"}},
248 },
249 },
250 },
251 },
252 newEPS: &discovery.EndpointSlice{
253 Endpoints: []discovery.Endpoint{
254 {
255 Hints: &discovery.EndpointHints{
256 ForZones: []discovery.ForZone{{Name: "zone-a"}},
257 },
258 },
259 {
260 Hints: &discovery.EndpointHints{
261 ForZones: []discovery.ForZone{{Name: "zone-b"}},
262 },
263 },
264 },
265 },
266 expectedEPS: &discovery.EndpointSlice{
267 Endpoints: []discovery.Endpoint{
268 {
269 Hints: &discovery.EndpointHints{
270 ForZones: []discovery.ForZone{{Name: "zone-a"}},
271 },
272 },
273 {
274 Hints: &discovery.EndpointHints{
275 ForZones: []discovery.ForZone{{Name: "zone-b"}},
276 },
277 },
278 },
279 },
280 },
281 }
282
283 for _, testcase := range testcases {
284 t.Run(testcase.name, func(t *testing.T) {
285 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TopologyAwareHints, testcase.hintsGateEnabled)()
286
287 dropDisabledFieldsOnUpdate(testcase.oldEPS, testcase.newEPS)
288 if !apiequality.Semantic.DeepEqual(testcase.newEPS, testcase.expectedEPS) {
289 t.Logf("actual endpointslice: %v", testcase.newEPS)
290 t.Logf("expected endpointslice: %v", testcase.expectedEPS)
291 t.Errorf("unexpected EndpointSlice from update API strategy")
292 }
293 })
294 }
295 }
296
297 func TestPrepareForUpdate(t *testing.T) {
298 testCases := []struct {
299 name string
300 oldEPS *discovery.EndpointSlice
301 newEPS *discovery.EndpointSlice
302 expectedEPS *discovery.EndpointSlice
303 }{
304 {
305 name: "unchanged EPS should not increment generation",
306 oldEPS: &discovery.EndpointSlice{
307 ObjectMeta: metav1.ObjectMeta{Generation: 1},
308 Endpoints: []discovery.Endpoint{{
309 Addresses: []string{"1.2.3.4"},
310 }},
311 },
312 newEPS: &discovery.EndpointSlice{
313 ObjectMeta: metav1.ObjectMeta{Generation: 1},
314 Endpoints: []discovery.Endpoint{{
315 Addresses: []string{"1.2.3.4"},
316 }},
317 },
318 expectedEPS: &discovery.EndpointSlice{
319 ObjectMeta: metav1.ObjectMeta{Generation: 1},
320 Endpoints: []discovery.Endpoint{{
321 Addresses: []string{"1.2.3.4"},
322 }},
323 },
324 },
325 {
326 name: "changed endpoints should increment generation",
327 oldEPS: &discovery.EndpointSlice{
328 ObjectMeta: metav1.ObjectMeta{Generation: 1},
329 Endpoints: []discovery.Endpoint{{
330 Addresses: []string{"1.2.3.4"},
331 }},
332 },
333 newEPS: &discovery.EndpointSlice{
334 ObjectMeta: metav1.ObjectMeta{Generation: 1},
335 Endpoints: []discovery.Endpoint{{
336 Addresses: []string{"1.2.3.5"},
337 }},
338 },
339 expectedEPS: &discovery.EndpointSlice{
340 ObjectMeta: metav1.ObjectMeta{Generation: 2},
341 Endpoints: []discovery.Endpoint{{
342 Addresses: []string{"1.2.3.5"},
343 }},
344 },
345 },
346 {
347 name: "changed labels should increment generation",
348 oldEPS: &discovery.EndpointSlice{
349 ObjectMeta: metav1.ObjectMeta{
350 Generation: 1,
351 Labels: map[string]string{"example": "one"},
352 },
353 Endpoints: []discovery.Endpoint{{
354 Addresses: []string{"1.2.3.4"},
355 }},
356 },
357 newEPS: &discovery.EndpointSlice{
358 ObjectMeta: metav1.ObjectMeta{
359 Generation: 1,
360 Labels: map[string]string{"example": "two"},
361 },
362 Endpoints: []discovery.Endpoint{{
363 Addresses: []string{"1.2.3.4"},
364 }},
365 },
366 expectedEPS: &discovery.EndpointSlice{
367 ObjectMeta: metav1.ObjectMeta{
368 Generation: 2,
369 Labels: map[string]string{"example": "two"},
370 },
371 Endpoints: []discovery.Endpoint{{
372 Addresses: []string{"1.2.3.4"},
373 }},
374 },
375 },
376 }
377
378 for _, tc := range testCases {
379 t.Run(tc.name, func(t *testing.T) {
380 Strategy.PrepareForUpdate(context.TODO(), tc.newEPS, tc.oldEPS)
381 if !apiequality.Semantic.DeepEqual(tc.newEPS, tc.expectedEPS) {
382 t.Errorf("Expected %+v\nGot: %+v", tc.expectedEPS, tc.newEPS)
383 }
384 })
385 }
386 }
387
388 func Test_dropTopologyOnV1(t *testing.T) {
389 testcases := []struct {
390 name string
391 v1Request bool
392 newEPS *discovery.EndpointSlice
393 originalEPS *discovery.EndpointSlice
394 expectedEPS *discovery.EndpointSlice
395 }{
396 {
397 name: "v1 request, without deprecated topology",
398 v1Request: true,
399 newEPS: &discovery.EndpointSlice{
400 Endpoints: []discovery.Endpoint{
401 {Hostname: utilpointer.String("hostname-1")},
402 {Hostname: utilpointer.String("hostname-1")},
403 },
404 },
405 expectedEPS: &discovery.EndpointSlice{
406 Endpoints: []discovery.Endpoint{
407 {Hostname: utilpointer.String("hostname-1")},
408 {Hostname: utilpointer.String("hostname-1")},
409 },
410 },
411 },
412 {
413 name: "v1beta1 request, without deprecated topology",
414 newEPS: &discovery.EndpointSlice{
415 Endpoints: []discovery.Endpoint{
416 {Hostname: utilpointer.String("hostname-1")},
417 {Hostname: utilpointer.String("hostname-1")},
418 },
419 },
420 expectedEPS: &discovery.EndpointSlice{
421 Endpoints: []discovery.Endpoint{
422 {Hostname: utilpointer.String("hostname-1")},
423 {Hostname: utilpointer.String("hostname-1")},
424 },
425 },
426 },
427 {
428 name: "v1 request, with deprecated topology",
429 v1Request: true,
430 newEPS: &discovery.EndpointSlice{
431 Endpoints: []discovery.Endpoint{
432 {DeprecatedTopology: map[string]string{"key": "value"}},
433 {DeprecatedTopology: map[string]string{"key": "value"}},
434 },
435 },
436 expectedEPS: &discovery.EndpointSlice{
437 Endpoints: []discovery.Endpoint{{}, {}},
438 },
439 },
440 {
441 name: "v1beta1 request, with deprecated topology",
442 newEPS: &discovery.EndpointSlice{
443 Endpoints: []discovery.Endpoint{
444 {DeprecatedTopology: map[string]string{"key": "value"}},
445 {DeprecatedTopology: map[string]string{"key": "value"}},
446 },
447 },
448 expectedEPS: &discovery.EndpointSlice{
449 Endpoints: []discovery.Endpoint{
450 {DeprecatedTopology: map[string]string{"key": "value"}},
451 {DeprecatedTopology: map[string]string{"key": "value"}},
452 },
453 },
454 },
455 {
456 name: "v1 request, updated metadata",
457 v1Request: true,
458 originalEPS: &discovery.EndpointSlice{
459 Endpoints: []discovery.Endpoint{
460 {
461 NodeName: utilpointer.String("node-1"),
462 DeprecatedTopology: map[string]string{"key": "value"},
463 },
464 {
465 NodeName: utilpointer.String("node-1"),
466 DeprecatedTopology: map[string]string{"key": "value"},
467 },
468 },
469 },
470 newEPS: &discovery.EndpointSlice{
471 ObjectMeta: metav1.ObjectMeta{
472 Labels: map[string]string{"example": "one"},
473 },
474 Endpoints: []discovery.Endpoint{
475 {
476 NodeName: utilpointer.String("node-1"),
477 DeprecatedTopology: map[string]string{"key": "value"},
478 },
479 {
480 NodeName: utilpointer.String("node-1"),
481 DeprecatedTopology: map[string]string{"key": "value"},
482 },
483 },
484 },
485 expectedEPS: &discovery.EndpointSlice{
486 ObjectMeta: metav1.ObjectMeta{
487 Labels: map[string]string{"example": "one"},
488 },
489 Endpoints: []discovery.Endpoint{
490 {
491 NodeName: utilpointer.String("node-1"),
492 DeprecatedTopology: map[string]string{"key": "value"},
493 },
494 {
495 NodeName: utilpointer.String("node-1"),
496 DeprecatedTopology: map[string]string{"key": "value"},
497 },
498 },
499 },
500 },
501 {
502 name: "v1beta1 request, updated metadata",
503 originalEPS: &discovery.EndpointSlice{
504 Endpoints: []discovery.Endpoint{
505 {
506 NodeName: utilpointer.String("node-1"),
507 DeprecatedTopology: map[string]string{"key": "value"},
508 },
509 {
510 NodeName: utilpointer.String("node-1"),
511 DeprecatedTopology: map[string]string{"key": "value"},
512 },
513 },
514 },
515 newEPS: &discovery.EndpointSlice{
516 ObjectMeta: metav1.ObjectMeta{
517 Labels: map[string]string{"example": "one"},
518 },
519 Endpoints: []discovery.Endpoint{
520 {
521 NodeName: utilpointer.String("node-1"),
522 DeprecatedTopology: map[string]string{"key": "value"},
523 },
524 {
525 NodeName: utilpointer.String("node-1"),
526 DeprecatedTopology: map[string]string{"key": "value"},
527 },
528 },
529 },
530 expectedEPS: &discovery.EndpointSlice{
531 ObjectMeta: metav1.ObjectMeta{
532 Labels: map[string]string{"example": "one"},
533 },
534 Endpoints: []discovery.Endpoint{
535 {
536 NodeName: utilpointer.String("node-1"),
537 DeprecatedTopology: map[string]string{"key": "value"},
538 },
539 {
540 NodeName: utilpointer.String("node-1"),
541 DeprecatedTopology: map[string]string{"key": "value"},
542 },
543 },
544 },
545 },
546 {
547 name: "v1 request, updated endpoints",
548 v1Request: true,
549 originalEPS: &discovery.EndpointSlice{
550 Endpoints: []discovery.Endpoint{
551 {DeprecatedTopology: map[string]string{"key": "value"}},
552 {DeprecatedTopology: map[string]string{"key": "value"}},
553 },
554 },
555 newEPS: &discovery.EndpointSlice{
556 Endpoints: []discovery.Endpoint{
557 {
558 Hostname: utilpointer.String("hostname-1"),
559 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
560 },
561 {
562 Hostname: utilpointer.String("hostname-1"),
563 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
564 },
565 },
566 },
567 expectedEPS: &discovery.EndpointSlice{
568 Endpoints: []discovery.Endpoint{
569 {Hostname: utilpointer.String("hostname-1")},
570 {Hostname: utilpointer.String("hostname-1")},
571 },
572 },
573 },
574 {
575 name: "v1beta1 request, updated endpoints",
576 originalEPS: &discovery.EndpointSlice{
577 Endpoints: []discovery.Endpoint{
578 {DeprecatedTopology: map[string]string{"key": "value"}},
579 {DeprecatedTopology: map[string]string{"key": "value"}},
580 },
581 },
582 newEPS: &discovery.EndpointSlice{
583 Endpoints: []discovery.Endpoint{
584 {
585 Hostname: utilpointer.String("hostname-1"),
586 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
587 },
588 {
589 Hostname: utilpointer.String("hostname-1"),
590 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
591 },
592 },
593 },
594 expectedEPS: &discovery.EndpointSlice{
595 Endpoints: []discovery.Endpoint{
596 {
597 Hostname: utilpointer.String("hostname-1"),
598 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
599 },
600 {
601 Hostname: utilpointer.String("hostname-1"),
602 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
603 },
604 },
605 },
606 },
607 {
608 name: "v1 request, updated endpoints with topology node names + other topology fields",
609 v1Request: true,
610 originalEPS: &discovery.EndpointSlice{
611 Endpoints: []discovery.Endpoint{
612 {
613 Hostname: utilpointer.String("hostname-1"),
614 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "other": "value"},
615 },
616 {
617 Hostname: utilpointer.String("hostname-1"),
618 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "foo": "bar"},
619 },
620 },
621 },
622 newEPS: &discovery.EndpointSlice{
623 Endpoints: []discovery.Endpoint{
624 {
625 Hostname: utilpointer.String("hostname-1a"),
626 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "other": "value"},
627 },
628 {
629 Hostname: utilpointer.String("hostname-1b"),
630 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "foo": "bar"},
631 },
632 },
633 },
634 expectedEPS: &discovery.EndpointSlice{
635 Endpoints: []discovery.Endpoint{
636 {
637 Hostname: utilpointer.String("hostname-1a"),
638 NodeName: utilpointer.String("node-1"),
639 },
640 {
641 Hostname: utilpointer.String("hostname-1b"),
642 NodeName: utilpointer.String("node-1"),
643 },
644 },
645 },
646 },
647 {
648 name: "v1 request, updated endpoints with topology node names",
649 v1Request: true,
650 originalEPS: &discovery.EndpointSlice{
651 Endpoints: []discovery.Endpoint{
652 {
653 Hostname: utilpointer.String("hostname-1"),
654 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
655 },
656 {
657 Hostname: utilpointer.String("hostname-1"),
658 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
659 },
660 },
661 },
662 newEPS: &discovery.EndpointSlice{
663 Endpoints: []discovery.Endpoint{
664 {
665 Hostname: utilpointer.String("hostname-1a"),
666 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
667 },
668 {
669 Hostname: utilpointer.String("hostname-1b"),
670 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
671 },
672 },
673 },
674 expectedEPS: &discovery.EndpointSlice{
675 Endpoints: []discovery.Endpoint{
676 {
677 Hostname: utilpointer.String("hostname-1a"),
678 NodeName: utilpointer.String("node-1"),
679 },
680 {
681 Hostname: utilpointer.String("hostname-1b"),
682 NodeName: utilpointer.String("node-1"),
683 },
684 },
685 },
686 },
687 {
688 name: "v1 request, updated endpoints with topology node names swapped",
689 v1Request: true,
690 originalEPS: &discovery.EndpointSlice{
691 Endpoints: []discovery.Endpoint{
692 {
693 Hostname: utilpointer.String("hostname-1"),
694 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
695 },
696 {
697 Hostname: utilpointer.String("hostname-1"),
698 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-2"},
699 },
700 },
701 },
702 newEPS: &discovery.EndpointSlice{
703 Endpoints: []discovery.Endpoint{
704 {
705 Hostname: utilpointer.String("hostname-1a"),
706 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-2"},
707 },
708 {
709 Hostname: utilpointer.String("hostname-1b"),
710 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
711 },
712 },
713 },
714 expectedEPS: &discovery.EndpointSlice{
715 Endpoints: []discovery.Endpoint{
716 {
717 Hostname: utilpointer.String("hostname-1a"),
718 NodeName: utilpointer.String("node-2"),
719 },
720 {
721 Hostname: utilpointer.String("hostname-1b"),
722 NodeName: utilpointer.String("node-1"),
723 },
724 },
725 },
726 },
727 {
728 name: "v1 request, updated endpoints with new topology node name",
729 v1Request: true,
730 originalEPS: &discovery.EndpointSlice{
731 Endpoints: []discovery.Endpoint{
732 {
733 Hostname: utilpointer.String("hostname-1"),
734 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
735 },
736 {
737 Hostname: utilpointer.String("hostname-1"),
738 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-2"},
739 },
740 },
741 },
742 newEPS: &discovery.EndpointSlice{
743 Endpoints: []discovery.Endpoint{
744 {
745 Hostname: utilpointer.String("hostname-1a"),
746
747 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-3"},
748 },
749 {
750 Hostname: utilpointer.String("hostname-1b"),
751 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
752 },
753 },
754 },
755 expectedEPS: &discovery.EndpointSlice{
756 Endpoints: []discovery.Endpoint{
757 {
758 Hostname: utilpointer.String("hostname-1a"),
759 },
760 {
761 Hostname: utilpointer.String("hostname-1b"),
762 NodeName: utilpointer.String("node-1"),
763 },
764 },
765 },
766 },
767 {
768 name: "v1 request, updated endpoints with topology node names + 1 new node name",
769 v1Request: true,
770 originalEPS: &discovery.EndpointSlice{
771 Endpoints: []discovery.Endpoint{
772 {
773 Hostname: utilpointer.String("hostname-1"),
774 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
775 },
776 {
777 Hostname: utilpointer.String("hostname-1"),
778 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
779 },
780 },
781 },
782 newEPS: &discovery.EndpointSlice{
783 Endpoints: []discovery.Endpoint{
784 {
785 Hostname: utilpointer.String("hostname-1a"),
786 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
787 },
788 {
789 Hostname: utilpointer.String("hostname-1b"),
790 NodeName: utilpointer.String("node-2"),
791 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
792 },
793 },
794 },
795 expectedEPS: &discovery.EndpointSlice{
796 Endpoints: []discovery.Endpoint{
797 {
798 Hostname: utilpointer.String("hostname-1a"),
799 NodeName: utilpointer.String("node-1"),
800 },
801 {
802 Hostname: utilpointer.String("hostname-1b"),
803 NodeName: utilpointer.String("node-2"),
804 },
805 },
806 },
807 },
808 {
809 name: "v1 request, updated endpoints with topology node names + new node names",
810 v1Request: true,
811 originalEPS: &discovery.EndpointSlice{
812 Endpoints: []discovery.Endpoint{
813 {
814 Hostname: utilpointer.String("hostname-1"),
815 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
816 },
817 {
818 Hostname: utilpointer.String("hostname-1"),
819 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
820 },
821 },
822 },
823 newEPS: &discovery.EndpointSlice{
824 Endpoints: []discovery.Endpoint{
825 {
826 Hostname: utilpointer.String("hostname-1a"),
827 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
828 NodeName: utilpointer.String("node-1"),
829 },
830 {
831 Hostname: utilpointer.String("hostname-1b"),
832 NodeName: utilpointer.String("node-2"),
833 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
834 },
835 },
836 },
837 expectedEPS: &discovery.EndpointSlice{
838 Endpoints: []discovery.Endpoint{
839 {
840 Hostname: utilpointer.String("hostname-1a"),
841 NodeName: utilpointer.String("node-1"),
842 },
843 {
844 Hostname: utilpointer.String("hostname-1b"),
845 NodeName: utilpointer.String("node-2"),
846 },
847 },
848 },
849 },
850 {
851 name: "v1 request, invalid node name label",
852 v1Request: true,
853 originalEPS: &discovery.EndpointSlice{
854 Endpoints: []discovery.Endpoint{
855 {
856 Hostname: utilpointer.String("hostname-1"),
857 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-1"},
858 },
859 {
860 Hostname: utilpointer.String("hostname-2"),
861 DeprecatedTopology: map[string]string{corev1.LabelHostname: "invalid node-2"},
862 },
863 {
864 Hostname: utilpointer.String("hostname-3"),
865 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-3"},
866 },
867 {
868 Hostname: utilpointer.String("hostname-4"),
869 DeprecatedTopology: map[string]string{corev1.LabelHostname: "invalid node-4"},
870 },
871 },
872 },
873 newEPS: &discovery.EndpointSlice{
874 Endpoints: []discovery.Endpoint{
875 {
876 Hostname: utilpointer.String("hostname-1"),
877 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-1"},
878 },
879 {
880 Hostname: utilpointer.String("hostname-2"),
881 DeprecatedTopology: map[string]string{corev1.LabelHostname: "invalid node-2"},
882 },
883 {
884 Hostname: utilpointer.String("hostname-3"),
885 NodeName: utilpointer.String("node-3"),
886 },
887 {
888 Hostname: utilpointer.String("hostname-4"),
889 NodeName: utilpointer.String("node-4"),
890 },
891 },
892 },
893 expectedEPS: &discovery.EndpointSlice{
894 Endpoints: []discovery.Endpoint{
895 {
896 Hostname: utilpointer.String("hostname-1"),
897 NodeName: utilpointer.String("valid-node-1"),
898 },
899 {
900 Hostname: utilpointer.String("hostname-2"),
901 },
902 {
903 Hostname: utilpointer.String("hostname-3"),
904 NodeName: utilpointer.String("node-3"),
905 },
906 {
907 Hostname: utilpointer.String("hostname-4"),
908 NodeName: utilpointer.String("node-4"),
909 },
910 },
911 },
912 },
913 }
914
915 for _, tc := range testcases {
916 t.Run(tc.name, func(t *testing.T) {
917 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{APIGroup: "discovery.k8s.io", APIVersion: "v1beta1", Resource: "endpointslices"})
918 if tc.v1Request {
919 ctx = genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{APIGroup: "discovery.k8s.io", APIVersion: "v1", Resource: "endpointslices"})
920 }
921
922 dropTopologyOnV1(ctx, tc.originalEPS, tc.newEPS)
923 if !apiequality.Semantic.DeepEqual(tc.newEPS, tc.expectedEPS) {
924 t.Logf("actual endpointslice: %v", tc.newEPS)
925 t.Logf("expected endpointslice: %v", tc.expectedEPS)
926 t.Errorf("unexpected EndpointSlice on API topology strategy")
927 }
928 })
929 }
930 }
931
932 func Test_getDeprecatedTopologyNodeNames(t *testing.T) {
933 testcases := []struct {
934 name string
935 endpointSlice *discovery.EndpointSlice
936 expectedNodeNames sets.String
937 }{
938 {
939 name: "2 nodes",
940 endpointSlice: &discovery.EndpointSlice{
941 Endpoints: []discovery.Endpoint{
942 {DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}},
943 {DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-2"}},
944 },
945 },
946 expectedNodeNames: sets.NewString("node-1", "node-2"),
947 },
948 {
949 name: "duplicate values",
950 endpointSlice: &discovery.EndpointSlice{
951 Endpoints: []discovery.Endpoint{
952 {DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}},
953 {DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-3"}},
954 {DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-3"}},
955 },
956 },
957 expectedNodeNames: sets.NewString("node-1", "node-3"),
958 },
959 {
960 name: "unset",
961 endpointSlice: &discovery.EndpointSlice{
962 Endpoints: []discovery.Endpoint{
963 {DeprecatedTopology: map[string]string{"other": "value"}},
964 {DeprecatedTopology: map[string]string{"foo": "bar"}},
965 {DeprecatedTopology: nil},
966 },
967 },
968 expectedNodeNames: sets.NewString(),
969 },
970 }
971
972 for _, tc := range testcases {
973 t.Run(tc.name, func(t *testing.T) {
974 actualNames := getDeprecatedTopologyNodeNames(tc.endpointSlice)
975 if !tc.expectedNodeNames.Equal(actualNames) {
976 t.Errorf("Expected %+v node names, got %+v", tc.expectedNodeNames, actualNames)
977 }
978 })
979 }
980 }
981
982 func TestWarningsOnEndpointSliceAddressType(t *testing.T) {
983 tests := []struct {
984 name string
985 addressType discovery.AddressType
986 wantWarning bool
987 }{
988 {
989 name: "AddressType = FQDN",
990 addressType: discovery.AddressTypeFQDN,
991 wantWarning: true,
992 },
993 {
994 name: "AddressType = IPV4",
995 addressType: discovery.AddressTypeIPv4,
996 wantWarning: false,
997 },
998 {
999 name: "AddressType = IPV6",
1000 addressType: discovery.AddressTypeIPv6,
1001 wantWarning: false,
1002 },
1003 }
1004 for _, tc := range tests {
1005 t.Run(tc.name, func(t *testing.T) {
1006 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{APIGroup: "discovery.k8s.io", APIVersion: "v1", Resource: "endpointslices"})
1007 edp := discovery.EndpointSlice{AddressType: tc.addressType}
1008 got := Strategy.WarningsOnCreate(ctx, &edp)
1009 if tc.wantWarning && len(got) == 0 {
1010 t.Fatal("Failed warning was not returned")
1011 } else if !tc.wantWarning && len(got) != 0 {
1012 t.Fatalf("Failed warning was returned (%v)", got)
1013 }
1014 })
1015 }
1016 }
1017
View as plain text