k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/discovery/endpointslice/strategy_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 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 ptr "k8s.io/utils/ptr" 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: ptr.To("node-1"), 48 }, 49 { 50 NodeName: ptr.To("node-2"), 51 }, 52 }, 53 }, 54 expectedEPS: &discovery.EndpointSlice{ 55 Endpoints: []discovery.Endpoint{ 56 { 57 NodeName: ptr.To("node-1"), 58 }, 59 { 60 NodeName: ptr.To("node-2"), 61 }, 62 }, 63 }, 64 }, 65 } 66 67 for _, testcase := range testcases { 68 t.Run(testcase.name, func(t *testing.T) { 69 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: ptr.To("node-1"), 105 }, 106 { 107 NodeName: ptr.To("node-2"), 108 }, 109 }, 110 }, 111 expectedEPS: &discovery.EndpointSlice{ 112 Endpoints: []discovery.Endpoint{ 113 { 114 NodeName: ptr.To("node-1"), 115 }, 116 { 117 NodeName: ptr.To("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: ptr.To("node-1-old"), 128 }, 129 { 130 NodeName: ptr.To("node-2-old"), 131 }, 132 }, 133 }, 134 newEPS: &discovery.EndpointSlice{ 135 Endpoints: []discovery.Endpoint{ 136 { 137 NodeName: ptr.To("node-1"), 138 }, 139 { 140 NodeName: ptr.To("node-2"), 141 }, 142 }, 143 }, 144 expectedEPS: &discovery.EndpointSlice{ 145 Endpoints: []discovery.Endpoint{ 146 { 147 NodeName: ptr.To("node-1"), 148 }, 149 { 150 NodeName: ptr.To("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 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: ptr.To("hostname-1")}, 402 {Hostname: ptr.To("hostname-1")}, 403 }, 404 }, 405 expectedEPS: &discovery.EndpointSlice{ 406 Endpoints: []discovery.Endpoint{ 407 {Hostname: ptr.To("hostname-1")}, 408 {Hostname: ptr.To("hostname-1")}, 409 }, 410 }, 411 }, 412 { 413 name: "v1beta1 request, without deprecated topology", 414 newEPS: &discovery.EndpointSlice{ 415 Endpoints: []discovery.Endpoint{ 416 {Hostname: ptr.To("hostname-1")}, 417 {Hostname: ptr.To("hostname-1")}, 418 }, 419 }, 420 expectedEPS: &discovery.EndpointSlice{ 421 Endpoints: []discovery.Endpoint{ 422 {Hostname: ptr.To("hostname-1")}, 423 {Hostname: ptr.To("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: ptr.To("node-1"), 462 DeprecatedTopology: map[string]string{"key": "value"}, 463 }, 464 { 465 NodeName: ptr.To("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: ptr.To("node-1"), 477 DeprecatedTopology: map[string]string{"key": "value"}, 478 }, 479 { 480 NodeName: ptr.To("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: ptr.To("node-1"), 492 DeprecatedTopology: map[string]string{"key": "value"}, 493 }, 494 { 495 NodeName: ptr.To("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: ptr.To("node-1"), 507 DeprecatedTopology: map[string]string{"key": "value"}, 508 }, 509 { 510 NodeName: ptr.To("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: ptr.To("node-1"), 522 DeprecatedTopology: map[string]string{"key": "value"}, 523 }, 524 { 525 NodeName: ptr.To("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: ptr.To("node-1"), 537 DeprecatedTopology: map[string]string{"key": "value"}, 538 }, 539 { 540 NodeName: ptr.To("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: ptr.To("hostname-1"), 559 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 560 }, 561 { 562 Hostname: ptr.To("hostname-1"), 563 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 564 }, 565 }, 566 }, 567 expectedEPS: &discovery.EndpointSlice{ 568 Endpoints: []discovery.Endpoint{ 569 {Hostname: ptr.To("hostname-1")}, 570 {Hostname: ptr.To("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: ptr.To("hostname-1"), 586 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 587 }, 588 { 589 Hostname: ptr.To("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: ptr.To("hostname-1"), 598 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 599 }, 600 { 601 Hostname: ptr.To("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: ptr.To("hostname-1"), 614 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "other": "value"}, 615 }, 616 { 617 Hostname: ptr.To("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: ptr.To("hostname-1a"), 626 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1", "other": "value"}, 627 }, 628 { 629 Hostname: ptr.To("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: ptr.To("hostname-1a"), 638 NodeName: ptr.To("node-1"), 639 }, 640 { 641 Hostname: ptr.To("hostname-1b"), 642 NodeName: ptr.To("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: ptr.To("hostname-1"), 654 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 655 }, 656 { 657 Hostname: ptr.To("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: ptr.To("hostname-1a"), 666 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 667 }, 668 { 669 Hostname: ptr.To("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: ptr.To("hostname-1a"), 678 NodeName: ptr.To("node-1"), 679 }, 680 { 681 Hostname: ptr.To("hostname-1b"), 682 NodeName: ptr.To("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: ptr.To("hostname-1"), 694 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 695 }, 696 { 697 Hostname: ptr.To("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: ptr.To("hostname-1a"), 706 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-2"}, 707 }, 708 { 709 Hostname: ptr.To("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: ptr.To("hostname-1a"), 718 NodeName: ptr.To("node-2"), 719 }, 720 { 721 Hostname: ptr.To("hostname-1b"), 722 NodeName: ptr.To("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: ptr.To("hostname-1"), 734 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 735 }, 736 { 737 Hostname: ptr.To("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: ptr.To("hostname-1a"), 746 // Invalid node name because it did not exist in previous version of EndpointSlice 747 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-3"}, 748 }, 749 { 750 Hostname: ptr.To("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: ptr.To("hostname-1a"), 759 }, 760 { 761 Hostname: ptr.To("hostname-1b"), 762 NodeName: ptr.To("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: ptr.To("hostname-1"), 774 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 775 }, 776 { 777 Hostname: ptr.To("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: ptr.To("hostname-1a"), 786 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 787 }, 788 { 789 Hostname: ptr.To("hostname-1b"), 790 NodeName: ptr.To("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: ptr.To("hostname-1a"), 799 NodeName: ptr.To("node-1"), 800 }, 801 { 802 Hostname: ptr.To("hostname-1b"), 803 NodeName: ptr.To("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: ptr.To("hostname-1"), 815 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 816 }, 817 { 818 Hostname: ptr.To("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: ptr.To("hostname-1a"), 827 DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"}, 828 NodeName: ptr.To("node-1"), 829 }, 830 { 831 Hostname: ptr.To("hostname-1b"), 832 NodeName: ptr.To("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: ptr.To("hostname-1a"), 841 NodeName: ptr.To("node-1"), 842 }, 843 { 844 Hostname: ptr.To("hostname-1b"), 845 NodeName: ptr.To("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: ptr.To("hostname-1"), 857 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-1"}, 858 }, 859 { 860 Hostname: ptr.To("hostname-2"), 861 DeprecatedTopology: map[string]string{corev1.LabelHostname: "invalid node-2"}, 862 }, 863 { 864 Hostname: ptr.To("hostname-3"), 865 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-3"}, 866 }, 867 { 868 Hostname: ptr.To("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: ptr.To("hostname-1"), 877 DeprecatedTopology: map[string]string{corev1.LabelHostname: "valid-node-1"}, 878 }, 879 { 880 Hostname: ptr.To("hostname-2"), 881 DeprecatedTopology: map[string]string{corev1.LabelHostname: "invalid node-2"}, 882 }, 883 { 884 Hostname: ptr.To("hostname-3"), 885 NodeName: ptr.To("node-3"), 886 }, 887 { 888 Hostname: ptr.To("hostname-4"), 889 NodeName: ptr.To("node-4"), 890 }, 891 }, 892 }, 893 expectedEPS: &discovery.EndpointSlice{ 894 Endpoints: []discovery.Endpoint{ 895 { 896 Hostname: ptr.To("hostname-1"), 897 NodeName: ptr.To("valid-node-1"), 898 }, 899 { 900 Hostname: ptr.To("hostname-2"), 901 }, 902 { 903 Hostname: ptr.To("hostname-3"), 904 NodeName: ptr.To("node-3"), 905 }, 906 { 907 Hostname: ptr.To("hostname-4"), 908 NodeName: ptr.To("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 }