k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/topologymanager/policy_test.go (about) 1 /* 2 Copyright 2019 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 topologymanager 18 19 import ( 20 "reflect" 21 "testing" 22 23 "k8s.io/api/core/v1" 24 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask" 25 ) 26 27 type policyMergeTestCase struct { 28 name string 29 hp []HintProvider 30 expected TopologyHint 31 } 32 33 func commonPolicyMergeTestCases(numaNodes []int) []policyMergeTestCase { 34 return []policyMergeTestCase{ 35 { 36 name: "Two providers, 1 hint each, same mask, both preferred 1/2", 37 hp: []HintProvider{ 38 &mockHintProvider{ 39 map[string][]TopologyHint{ 40 "resource1": { 41 { 42 NUMANodeAffinity: NewTestBitMask(0), 43 Preferred: true, 44 }, 45 }, 46 }, 47 }, 48 &mockHintProvider{ 49 map[string][]TopologyHint{ 50 "resource2": { 51 { 52 NUMANodeAffinity: NewTestBitMask(0), 53 Preferred: true, 54 }, 55 }, 56 }, 57 }, 58 }, 59 expected: TopologyHint{ 60 NUMANodeAffinity: NewTestBitMask(0), 61 Preferred: true, 62 }, 63 }, 64 { 65 name: "Two providers, 1 hint each, same mask, both preferred 2/2", 66 hp: []HintProvider{ 67 &mockHintProvider{ 68 map[string][]TopologyHint{ 69 "resource1": { 70 { 71 NUMANodeAffinity: NewTestBitMask(1), 72 Preferred: true, 73 }, 74 }, 75 }, 76 }, 77 &mockHintProvider{ 78 map[string][]TopologyHint{ 79 "resource2": { 80 { 81 NUMANodeAffinity: NewTestBitMask(1), 82 Preferred: true, 83 }, 84 }, 85 }, 86 }, 87 }, 88 expected: TopologyHint{ 89 NUMANodeAffinity: NewTestBitMask(1), 90 Preferred: true, 91 }, 92 }, 93 { 94 name: "Two providers, 1 no hints, 1 single hint preferred 1/2", 95 hp: []HintProvider{ 96 &mockHintProvider{}, 97 &mockHintProvider{ 98 map[string][]TopologyHint{ 99 "resource": { 100 { 101 NUMANodeAffinity: NewTestBitMask(0), 102 Preferred: true, 103 }, 104 }, 105 }, 106 }, 107 }, 108 expected: TopologyHint{ 109 NUMANodeAffinity: NewTestBitMask(0), 110 Preferred: true, 111 }, 112 }, 113 { 114 name: "Two providers, 1 no hints, 1 single hint preferred 2/2", 115 hp: []HintProvider{ 116 &mockHintProvider{}, 117 &mockHintProvider{ 118 map[string][]TopologyHint{ 119 "resource": { 120 { 121 NUMANodeAffinity: NewTestBitMask(1), 122 Preferred: true, 123 }, 124 }, 125 }, 126 }, 127 }, 128 expected: TopologyHint{ 129 NUMANodeAffinity: NewTestBitMask(1), 130 Preferred: true, 131 }, 132 }, 133 { 134 name: "Two providers, 1 with 2 hints, 1 with single hint matching 1/2", 135 hp: []HintProvider{ 136 &mockHintProvider{ 137 map[string][]TopologyHint{ 138 "resource1": { 139 { 140 NUMANodeAffinity: NewTestBitMask(0), 141 Preferred: true, 142 }, 143 { 144 NUMANodeAffinity: NewTestBitMask(1), 145 Preferred: true, 146 }, 147 }, 148 }, 149 }, 150 &mockHintProvider{ 151 map[string][]TopologyHint{ 152 "resource2": { 153 { 154 NUMANodeAffinity: NewTestBitMask(0), 155 Preferred: true, 156 }, 157 }, 158 }, 159 }, 160 }, 161 expected: TopologyHint{ 162 NUMANodeAffinity: NewTestBitMask(0), 163 Preferred: true, 164 }, 165 }, 166 { 167 name: "Two providers, 1 with 2 hints, 1 with single hint matching 2/2", 168 hp: []HintProvider{ 169 &mockHintProvider{ 170 map[string][]TopologyHint{ 171 "resource1": { 172 { 173 NUMANodeAffinity: NewTestBitMask(0), 174 Preferred: true, 175 }, 176 { 177 NUMANodeAffinity: NewTestBitMask(1), 178 Preferred: true, 179 }, 180 }, 181 }, 182 }, 183 &mockHintProvider{ 184 map[string][]TopologyHint{ 185 "resource2": { 186 { 187 NUMANodeAffinity: NewTestBitMask(1), 188 Preferred: true, 189 }, 190 }, 191 }, 192 }, 193 }, 194 expected: TopologyHint{ 195 NUMANodeAffinity: NewTestBitMask(1), 196 Preferred: true, 197 }, 198 }, 199 { 200 name: "Two providers, both with 2 hints, matching narrower preferred hint from both", 201 hp: []HintProvider{ 202 &mockHintProvider{ 203 map[string][]TopologyHint{ 204 "resource1": { 205 { 206 NUMANodeAffinity: NewTestBitMask(0), 207 Preferred: true, 208 }, 209 { 210 NUMANodeAffinity: NewTestBitMask(1), 211 Preferred: true, 212 }, 213 }, 214 }, 215 }, 216 &mockHintProvider{ 217 map[string][]TopologyHint{ 218 "resource2": { 219 { 220 NUMANodeAffinity: NewTestBitMask(0), 221 Preferred: true, 222 }, 223 { 224 NUMANodeAffinity: NewTestBitMask(0, 1), 225 Preferred: false, 226 }, 227 }, 228 }, 229 }, 230 }, 231 expected: TopologyHint{ 232 NUMANodeAffinity: NewTestBitMask(0), 233 Preferred: true, 234 }, 235 }, 236 { 237 name: "Ensure less narrow preferred hints are chosen over narrower non-preferred hints", 238 hp: []HintProvider{ 239 &mockHintProvider{ 240 map[string][]TopologyHint{ 241 "resource1": { 242 { 243 NUMANodeAffinity: NewTestBitMask(1), 244 Preferred: true, 245 }, 246 { 247 NUMANodeAffinity: NewTestBitMask(0, 1), 248 Preferred: false, 249 }, 250 }, 251 }, 252 }, 253 &mockHintProvider{ 254 map[string][]TopologyHint{ 255 "resource2": { 256 { 257 NUMANodeAffinity: NewTestBitMask(0), 258 Preferred: true, 259 }, 260 { 261 NUMANodeAffinity: NewTestBitMask(1), 262 Preferred: true, 263 }, 264 { 265 NUMANodeAffinity: NewTestBitMask(0, 1), 266 Preferred: false, 267 }, 268 }, 269 }, 270 }, 271 }, 272 expected: TopologyHint{ 273 NUMANodeAffinity: NewTestBitMask(1), 274 Preferred: true, 275 }, 276 }, 277 { 278 name: "Multiple resources, same provider", 279 hp: []HintProvider{ 280 &mockHintProvider{ 281 map[string][]TopologyHint{ 282 "resource1": { 283 { 284 NUMANodeAffinity: NewTestBitMask(1), 285 Preferred: true, 286 }, 287 { 288 NUMANodeAffinity: NewTestBitMask(0, 1), 289 Preferred: false, 290 }, 291 }, 292 "resource2": { 293 { 294 NUMANodeAffinity: NewTestBitMask(0), 295 Preferred: true, 296 }, 297 { 298 NUMANodeAffinity: NewTestBitMask(1), 299 Preferred: true, 300 }, 301 { 302 NUMANodeAffinity: NewTestBitMask(0, 1), 303 Preferred: false, 304 }, 305 }, 306 }, 307 }, 308 }, 309 expected: TopologyHint{ 310 NUMANodeAffinity: NewTestBitMask(1), 311 Preferred: true, 312 }, 313 }, 314 } 315 } 316 317 func (p *bestEffortPolicy) mergeTestCases(numaNodes []int) []policyMergeTestCase { 318 return []policyMergeTestCase{ 319 { 320 name: "Two providers, 2 hints each, same mask (some with different bits), same preferred", 321 hp: []HintProvider{ 322 &mockHintProvider{ 323 map[string][]TopologyHint{ 324 "resource1": { 325 { 326 NUMANodeAffinity: NewTestBitMask(0, 1), 327 Preferred: true, 328 }, 329 { 330 NUMANodeAffinity: NewTestBitMask(0, 2), 331 Preferred: true, 332 }, 333 }, 334 }, 335 }, 336 &mockHintProvider{ 337 map[string][]TopologyHint{ 338 "resource2": { 339 { 340 NUMANodeAffinity: NewTestBitMask(0, 1), 341 Preferred: true, 342 }, 343 { 344 NUMANodeAffinity: NewTestBitMask(0, 2), 345 Preferred: true, 346 }, 347 }, 348 }, 349 }, 350 }, 351 expected: TopologyHint{ 352 NUMANodeAffinity: NewTestBitMask(0, 1), 353 Preferred: true, 354 }, 355 }, 356 { 357 name: "TopologyHint not set", 358 hp: []HintProvider{}, 359 expected: TopologyHint{ 360 NUMANodeAffinity: NewTestBitMask(numaNodes...), 361 Preferred: true, 362 }, 363 }, 364 { 365 name: "HintProvider returns empty non-nil map[string][]TopologyHint", 366 hp: []HintProvider{ 367 &mockHintProvider{ 368 map[string][]TopologyHint{}, 369 }, 370 }, 371 expected: TopologyHint{ 372 NUMANodeAffinity: NewTestBitMask(numaNodes...), 373 Preferred: true, 374 }, 375 }, 376 { 377 name: "HintProvider returns -nil map[string][]TopologyHint from provider", 378 hp: []HintProvider{ 379 &mockHintProvider{ 380 map[string][]TopologyHint{ 381 "resource": nil, 382 }, 383 }, 384 }, 385 expected: TopologyHint{ 386 NUMANodeAffinity: NewTestBitMask(numaNodes...), 387 Preferred: true, 388 }, 389 }, 390 { 391 name: "HintProvider returns empty non-nil map[string][]TopologyHint from provider", hp: []HintProvider{ 392 &mockHintProvider{ 393 map[string][]TopologyHint{ 394 "resource": {}, 395 }, 396 }, 397 }, 398 expected: TopologyHint{ 399 NUMANodeAffinity: NewTestBitMask(numaNodes...), 400 Preferred: false, 401 }, 402 }, 403 { 404 name: "Single TopologyHint with Preferred as true and NUMANodeAffinity as nil", 405 hp: []HintProvider{ 406 &mockHintProvider{ 407 map[string][]TopologyHint{ 408 "resource": { 409 { 410 NUMANodeAffinity: nil, 411 Preferred: true, 412 }, 413 }, 414 }, 415 }, 416 }, 417 expected: TopologyHint{ 418 NUMANodeAffinity: NewTestBitMask(numaNodes...), 419 Preferred: true, 420 }, 421 }, 422 { 423 name: "Single TopologyHint with Preferred as false and NUMANodeAffinity as nil", 424 hp: []HintProvider{ 425 &mockHintProvider{ 426 map[string][]TopologyHint{ 427 "resource": { 428 { 429 NUMANodeAffinity: nil, 430 Preferred: false, 431 }, 432 }, 433 }, 434 }, 435 }, 436 expected: TopologyHint{ 437 NUMANodeAffinity: NewTestBitMask(numaNodes...), 438 Preferred: false, 439 }, 440 }, 441 { 442 name: "Two providers, 1 hint each, no common mask", 443 hp: []HintProvider{ 444 &mockHintProvider{ 445 map[string][]TopologyHint{ 446 "resource1": { 447 { 448 NUMANodeAffinity: NewTestBitMask(0), 449 Preferred: true, 450 }, 451 }, 452 }, 453 }, 454 &mockHintProvider{ 455 map[string][]TopologyHint{ 456 "resource2": { 457 { 458 NUMANodeAffinity: NewTestBitMask(1), 459 Preferred: true, 460 }, 461 }, 462 }, 463 }, 464 }, 465 expected: TopologyHint{ 466 NUMANodeAffinity: NewTestBitMask(numaNodes...), 467 Preferred: false, 468 }, 469 }, 470 { 471 name: "Two providers, 1 hint each, same mask, 1 preferred, 1 not 1/2", 472 hp: []HintProvider{ 473 &mockHintProvider{ 474 map[string][]TopologyHint{ 475 "resource1": { 476 { 477 NUMANodeAffinity: NewTestBitMask(0), 478 Preferred: true, 479 }, 480 }, 481 }, 482 }, 483 &mockHintProvider{ 484 map[string][]TopologyHint{ 485 "resource2": { 486 { 487 NUMANodeAffinity: NewTestBitMask(0), 488 Preferred: false, 489 }, 490 }, 491 }, 492 }, 493 }, 494 expected: TopologyHint{ 495 NUMANodeAffinity: NewTestBitMask(0), 496 Preferred: false, 497 }, 498 }, 499 { 500 name: "Two providers, 1 hint each, same mask, 1 preferred, 1 not 2/2", 501 hp: []HintProvider{ 502 &mockHintProvider{ 503 map[string][]TopologyHint{ 504 "resource1": { 505 { 506 NUMANodeAffinity: NewTestBitMask(1), 507 Preferred: true, 508 }, 509 }, 510 }, 511 }, 512 &mockHintProvider{ 513 map[string][]TopologyHint{ 514 "resource2": { 515 { 516 NUMANodeAffinity: NewTestBitMask(1), 517 Preferred: false, 518 }, 519 }, 520 }, 521 }, 522 }, 523 expected: TopologyHint{ 524 NUMANodeAffinity: NewTestBitMask(1), 525 Preferred: false, 526 }, 527 }, 528 { 529 name: "Two providers, 1 hint each, 1 wider mask, both preferred 1/2", 530 hp: []HintProvider{ 531 &mockHintProvider{ 532 map[string][]TopologyHint{ 533 "resource1": { 534 { 535 NUMANodeAffinity: NewTestBitMask(0), 536 Preferred: true, 537 }, 538 }, 539 }, 540 }, 541 &mockHintProvider{ 542 map[string][]TopologyHint{ 543 "resource2": { 544 { 545 NUMANodeAffinity: NewTestBitMask(0, 1), 546 Preferred: true, 547 }, 548 }, 549 }, 550 }, 551 }, 552 expected: TopologyHint{ 553 NUMANodeAffinity: NewTestBitMask(0), 554 Preferred: false, 555 }, 556 }, 557 { 558 name: "Two providers, 1 with 2 hints, 1 with single non-preferred hint matching", 559 hp: []HintProvider{ 560 &mockHintProvider{ 561 map[string][]TopologyHint{ 562 "resource1": { 563 { 564 NUMANodeAffinity: NewTestBitMask(0), 565 Preferred: true, 566 }, 567 { 568 NUMANodeAffinity: NewTestBitMask(1), 569 Preferred: true, 570 }, 571 }, 572 }, 573 }, 574 &mockHintProvider{ 575 map[string][]TopologyHint{ 576 "resource2": { 577 { 578 NUMANodeAffinity: NewTestBitMask(0, 1), 579 Preferred: false, 580 }, 581 }, 582 }, 583 }, 584 }, 585 expected: TopologyHint{ 586 NUMANodeAffinity: NewTestBitMask(0), 587 Preferred: false, 588 }, 589 }, 590 { 591 name: "Two providers, 1 hint each, 1 wider mask, both preferred 2/2", 592 hp: []HintProvider{ 593 &mockHintProvider{ 594 map[string][]TopologyHint{ 595 "resource1": { 596 { 597 NUMANodeAffinity: NewTestBitMask(1), 598 Preferred: true, 599 }, 600 }, 601 }, 602 }, 603 &mockHintProvider{ 604 map[string][]TopologyHint{ 605 "resource2": { 606 { 607 NUMANodeAffinity: NewTestBitMask(0, 1), 608 Preferred: true, 609 }, 610 }, 611 }, 612 }, 613 }, 614 expected: TopologyHint{ 615 NUMANodeAffinity: NewTestBitMask(1), 616 Preferred: false, 617 }, 618 }, 619 { 620 name: "bestNonPreferredAffinityCount (1)", 621 hp: []HintProvider{ 622 &mockHintProvider{ 623 map[string][]TopologyHint{ 624 "resource1": { 625 { 626 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 627 Preferred: false, 628 }, 629 { 630 NUMANodeAffinity: NewTestBitMask(0, 1), 631 Preferred: false, 632 }, 633 }, 634 }, 635 }, 636 &mockHintProvider{ 637 map[string][]TopologyHint{ 638 "resource2": { 639 { 640 NUMANodeAffinity: NewTestBitMask(0, 1), 641 Preferred: false, 642 }, 643 }, 644 }, 645 }, 646 }, 647 expected: TopologyHint{ 648 NUMANodeAffinity: NewTestBitMask(0, 1), 649 Preferred: false, 650 }, 651 }, 652 { 653 name: "bestNonPreferredAffinityCount (2)", 654 hp: []HintProvider{ 655 &mockHintProvider{ 656 map[string][]TopologyHint{ 657 "resource1": { 658 { 659 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 660 Preferred: false, 661 }, 662 { 663 NUMANodeAffinity: NewTestBitMask(0, 1), 664 Preferred: false, 665 }, 666 }, 667 }, 668 }, 669 &mockHintProvider{ 670 map[string][]TopologyHint{ 671 "resource2": { 672 { 673 NUMANodeAffinity: NewTestBitMask(0, 3), 674 Preferred: false, 675 }, 676 }, 677 }, 678 }, 679 }, 680 expected: TopologyHint{ 681 NUMANodeAffinity: NewTestBitMask(0, 3), 682 Preferred: false, 683 }, 684 }, 685 { 686 name: "bestNonPreferredAffinityCount (3)", 687 hp: []HintProvider{ 688 &mockHintProvider{ 689 map[string][]TopologyHint{ 690 "resource1": { 691 { 692 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 693 Preferred: false, 694 }, 695 { 696 NUMANodeAffinity: NewTestBitMask(0, 1), 697 Preferred: false, 698 }, 699 }, 700 }, 701 }, 702 &mockHintProvider{ 703 map[string][]TopologyHint{ 704 "resource2": { 705 { 706 NUMANodeAffinity: NewTestBitMask(1, 2), 707 Preferred: false, 708 }, 709 }, 710 }, 711 }, 712 }, 713 expected: TopologyHint{ 714 NUMANodeAffinity: NewTestBitMask(1, 2), 715 Preferred: false, 716 }, 717 }, 718 { 719 name: "bestNonPreferredAffinityCount (4)", 720 hp: []HintProvider{ 721 &mockHintProvider{ 722 map[string][]TopologyHint{ 723 "resource1": { 724 { 725 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 726 Preferred: false, 727 }, 728 { 729 NUMANodeAffinity: NewTestBitMask(0, 1), 730 Preferred: false, 731 }, 732 }, 733 }, 734 }, 735 &mockHintProvider{ 736 map[string][]TopologyHint{ 737 "resource2": { 738 { 739 NUMANodeAffinity: NewTestBitMask(2, 3), 740 Preferred: false, 741 }, 742 }, 743 }, 744 }, 745 }, 746 expected: TopologyHint{ 747 NUMANodeAffinity: NewTestBitMask(2, 3), 748 Preferred: false, 749 }, 750 }, 751 } 752 } 753 754 func (p *bestEffortPolicy) mergeTestCasesNoPolicies(numaNodes []int) []policyMergeTestCase { 755 return []policyMergeTestCase{ 756 { 757 name: "bestNonPreferredAffinityCount (5)", 758 hp: []HintProvider{ 759 &mockHintProvider{ 760 map[string][]TopologyHint{ 761 "resource1": { 762 { 763 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 764 Preferred: false, 765 }, 766 { 767 NUMANodeAffinity: NewTestBitMask(0, 1), 768 Preferred: false, 769 }, 770 }, 771 }, 772 }, 773 &mockHintProvider{ 774 map[string][]TopologyHint{ 775 "resource2": { 776 { 777 NUMANodeAffinity: NewTestBitMask(1, 2), 778 Preferred: false, 779 }, 780 { 781 NUMANodeAffinity: NewTestBitMask(2, 3), 782 Preferred: false, 783 }, 784 }, 785 }, 786 }, 787 }, 788 expected: TopologyHint{ 789 NUMANodeAffinity: NewTestBitMask(1, 2), 790 Preferred: false, 791 }, 792 }, 793 { 794 name: "bestNonPreferredAffinityCount (6)", 795 hp: []HintProvider{ 796 &mockHintProvider{ 797 map[string][]TopologyHint{ 798 "resource1": { 799 { 800 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 801 Preferred: false, 802 }, 803 { 804 NUMANodeAffinity: NewTestBitMask(0, 1), 805 Preferred: false, 806 }, 807 }, 808 }, 809 }, 810 &mockHintProvider{ 811 map[string][]TopologyHint{ 812 "resource2": { 813 { 814 NUMANodeAffinity: NewTestBitMask(1, 2, 3), 815 Preferred: false, 816 }, 817 { 818 NUMANodeAffinity: NewTestBitMask(1, 2), 819 Preferred: false, 820 }, 821 { 822 NUMANodeAffinity: NewTestBitMask(1, 3), 823 Preferred: false, 824 }, 825 { 826 NUMANodeAffinity: NewTestBitMask(2, 3), 827 Preferred: false, 828 }, 829 }, 830 }, 831 }, 832 }, 833 expected: TopologyHint{ 834 NUMANodeAffinity: NewTestBitMask(1, 2), 835 Preferred: false, 836 }, 837 }, 838 } 839 } 840 841 func (p *bestEffortPolicy) mergeTestCasesClosestNUMA(numaNodes []int) []policyMergeTestCase { 842 return []policyMergeTestCase{ 843 { 844 name: "Two providers, 2 hints each, same mask (some with different bits), same preferred", 845 hp: []HintProvider{ 846 &mockHintProvider{ 847 map[string][]TopologyHint{ 848 "resource1": { 849 { 850 NUMANodeAffinity: NewTestBitMask(0, 4), 851 Preferred: true, 852 }, 853 { 854 NUMANodeAffinity: NewTestBitMask(0, 2), 855 Preferred: true, 856 }, 857 }, 858 }, 859 }, 860 &mockHintProvider{ 861 map[string][]TopologyHint{ 862 "resource2": { 863 { 864 NUMANodeAffinity: NewTestBitMask(0, 4), 865 Preferred: true, 866 }, 867 { 868 NUMANodeAffinity: NewTestBitMask(0, 2), 869 Preferred: true, 870 }, 871 }, 872 }, 873 }, 874 }, 875 expected: TopologyHint{ 876 NUMANodeAffinity: NewTestBitMask(0, 2), 877 Preferred: true, 878 }, 879 }, 880 { 881 name: "Two providers, 2 hints each, different mask", 882 hp: []HintProvider{ 883 &mockHintProvider{ 884 map[string][]TopologyHint{ 885 "resource1": { 886 { 887 NUMANodeAffinity: NewTestBitMask(4), 888 Preferred: true, 889 }, 890 { 891 NUMANodeAffinity: NewTestBitMask(0, 2), 892 Preferred: true, 893 }, 894 }, 895 }, 896 }, 897 &mockHintProvider{ 898 map[string][]TopologyHint{ 899 "resource2": { 900 { 901 NUMANodeAffinity: NewTestBitMask(4), 902 Preferred: true, 903 }, 904 { 905 NUMANodeAffinity: NewTestBitMask(0, 2), 906 Preferred: true, 907 }, 908 }, 909 }, 910 }, 911 }, 912 expected: TopologyHint{ 913 NUMANodeAffinity: NewTestBitMask(4), 914 Preferred: true, 915 }, 916 }, 917 { 918 name: "bestNonPreferredAffinityCount (5)", 919 hp: []HintProvider{ 920 &mockHintProvider{ 921 map[string][]TopologyHint{ 922 "resource1": { 923 { 924 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 925 Preferred: false, 926 }, 927 { 928 NUMANodeAffinity: NewTestBitMask(0, 1), 929 Preferred: false, 930 }, 931 }, 932 }, 933 }, 934 &mockHintProvider{ 935 map[string][]TopologyHint{ 936 "resource2": { 937 { 938 NUMANodeAffinity: NewTestBitMask(1, 2), 939 Preferred: false, 940 }, 941 { 942 NUMANodeAffinity: NewTestBitMask(2, 3), 943 Preferred: false, 944 }, 945 }, 946 }, 947 }, 948 }, 949 expected: TopologyHint{ 950 NUMANodeAffinity: NewTestBitMask(2, 3), 951 Preferred: false, 952 }, 953 }, 954 { 955 name: "bestNonPreferredAffinityCount (6)", 956 hp: []HintProvider{ 957 &mockHintProvider{ 958 map[string][]TopologyHint{ 959 "resource1": { 960 { 961 NUMANodeAffinity: NewTestBitMask(0, 1, 2, 3), 962 Preferred: false, 963 }, 964 { 965 NUMANodeAffinity: NewTestBitMask(0, 1), 966 Preferred: false, 967 }, 968 }, 969 }, 970 }, 971 &mockHintProvider{ 972 map[string][]TopologyHint{ 973 "resource2": { 974 { 975 NUMANodeAffinity: NewTestBitMask(1, 2, 3), 976 Preferred: false, 977 }, 978 { 979 NUMANodeAffinity: NewTestBitMask(1, 2), 980 Preferred: false, 981 }, 982 { 983 NUMANodeAffinity: NewTestBitMask(1, 3), 984 Preferred: false, 985 }, 986 { 987 NUMANodeAffinity: NewTestBitMask(2, 3), 988 Preferred: false, 989 }, 990 }, 991 }, 992 }, 993 }, 994 expected: TopologyHint{ 995 NUMANodeAffinity: NewTestBitMask(2, 3), 996 Preferred: false, 997 }, 998 }, 999 } 1000 } 1001 1002 func (p *singleNumaNodePolicy) mergeTestCases(numaNodes []int) []policyMergeTestCase { 1003 return []policyMergeTestCase{ 1004 { 1005 name: "TopologyHint not set", 1006 hp: []HintProvider{}, 1007 expected: TopologyHint{ 1008 NUMANodeAffinity: nil, 1009 Preferred: true, 1010 }, 1011 }, 1012 { 1013 name: "HintProvider returns empty non-nil map[string][]TopologyHint", 1014 hp: []HintProvider{ 1015 &mockHintProvider{ 1016 map[string][]TopologyHint{}, 1017 }, 1018 }, 1019 expected: TopologyHint{ 1020 NUMANodeAffinity: nil, 1021 Preferred: true, 1022 }, 1023 }, 1024 { 1025 name: "HintProvider returns -nil map[string][]TopologyHint from provider", 1026 hp: []HintProvider{ 1027 &mockHintProvider{ 1028 map[string][]TopologyHint{ 1029 "resource": nil, 1030 }, 1031 }, 1032 }, 1033 expected: TopologyHint{ 1034 NUMANodeAffinity: nil, 1035 Preferred: true, 1036 }, 1037 }, 1038 { 1039 name: "HintProvider returns empty non-nil map[string][]TopologyHint from provider", hp: []HintProvider{ 1040 &mockHintProvider{ 1041 map[string][]TopologyHint{ 1042 "resource": {}, 1043 }, 1044 }, 1045 }, 1046 expected: TopologyHint{ 1047 NUMANodeAffinity: nil, 1048 Preferred: false, 1049 }, 1050 }, 1051 { 1052 name: "Single TopologyHint with Preferred as true and NUMANodeAffinity as nil", 1053 hp: []HintProvider{ 1054 &mockHintProvider{ 1055 map[string][]TopologyHint{ 1056 "resource": { 1057 { 1058 NUMANodeAffinity: nil, 1059 Preferred: true, 1060 }, 1061 }, 1062 }, 1063 }, 1064 }, 1065 expected: TopologyHint{ 1066 NUMANodeAffinity: nil, 1067 Preferred: true, 1068 }, 1069 }, 1070 { 1071 name: "Single TopologyHint with Preferred as false and NUMANodeAffinity as nil", 1072 hp: []HintProvider{ 1073 &mockHintProvider{ 1074 map[string][]TopologyHint{ 1075 "resource": { 1076 { 1077 NUMANodeAffinity: nil, 1078 Preferred: false, 1079 }, 1080 }, 1081 }, 1082 }, 1083 }, 1084 expected: TopologyHint{ 1085 NUMANodeAffinity: nil, 1086 Preferred: false, 1087 }, 1088 }, 1089 { 1090 name: "Two providers, 1 hint each, no common mask", 1091 hp: []HintProvider{ 1092 &mockHintProvider{ 1093 map[string][]TopologyHint{ 1094 "resource1": { 1095 { 1096 NUMANodeAffinity: NewTestBitMask(0), 1097 Preferred: true, 1098 }, 1099 }, 1100 }, 1101 }, 1102 &mockHintProvider{ 1103 map[string][]TopologyHint{ 1104 "resource2": { 1105 { 1106 NUMANodeAffinity: NewTestBitMask(1), 1107 Preferred: true, 1108 }, 1109 }, 1110 }, 1111 }, 1112 }, 1113 expected: TopologyHint{ 1114 NUMANodeAffinity: nil, 1115 Preferred: false, 1116 }, 1117 }, 1118 { 1119 name: "Two providers, 1 hint each, same mask, 1 preferred, 1 not 1/2", 1120 hp: []HintProvider{ 1121 &mockHintProvider{ 1122 map[string][]TopologyHint{ 1123 "resource1": { 1124 { 1125 NUMANodeAffinity: NewTestBitMask(0), 1126 Preferred: true, 1127 }, 1128 }, 1129 }, 1130 }, 1131 &mockHintProvider{ 1132 map[string][]TopologyHint{ 1133 "resource2": { 1134 { 1135 NUMANodeAffinity: NewTestBitMask(0), 1136 Preferred: false, 1137 }, 1138 }, 1139 }, 1140 }, 1141 }, 1142 expected: TopologyHint{ 1143 NUMANodeAffinity: nil, 1144 Preferred: false, 1145 }, 1146 }, 1147 { 1148 name: "Two providers, 1 hint each, same mask, 1 preferred, 1 not 2/2", 1149 hp: []HintProvider{ 1150 &mockHintProvider{ 1151 map[string][]TopologyHint{ 1152 "resource1": { 1153 { 1154 NUMANodeAffinity: NewTestBitMask(1), 1155 Preferred: true, 1156 }, 1157 }, 1158 }, 1159 }, 1160 &mockHintProvider{ 1161 map[string][]TopologyHint{ 1162 "resource2": { 1163 { 1164 NUMANodeAffinity: NewTestBitMask(1), 1165 Preferred: false, 1166 }, 1167 }, 1168 }, 1169 }, 1170 }, 1171 expected: TopologyHint{ 1172 NUMANodeAffinity: nil, 1173 Preferred: false, 1174 }, 1175 }, 1176 { 1177 name: "Two providers, 1 with 2 hints, 1 with single non-preferred hint matching", 1178 hp: []HintProvider{ 1179 &mockHintProvider{ 1180 map[string][]TopologyHint{ 1181 "resource1": { 1182 { 1183 NUMANodeAffinity: NewTestBitMask(0), 1184 Preferred: true, 1185 }, 1186 { 1187 NUMANodeAffinity: NewTestBitMask(1), 1188 Preferred: true, 1189 }, 1190 }, 1191 }, 1192 }, 1193 &mockHintProvider{ 1194 map[string][]TopologyHint{ 1195 "resource2": { 1196 { 1197 NUMANodeAffinity: NewTestBitMask(0, 1), 1198 Preferred: false, 1199 }, 1200 }, 1201 }, 1202 }, 1203 }, 1204 expected: TopologyHint{ 1205 NUMANodeAffinity: nil, 1206 Preferred: false, 1207 }, 1208 }, 1209 { 1210 name: "Single NUMA hint generation", 1211 hp: []HintProvider{ 1212 &mockHintProvider{ 1213 map[string][]TopologyHint{ 1214 "resource1": { 1215 { 1216 NUMANodeAffinity: NewTestBitMask(0, 1), 1217 Preferred: true, 1218 }, 1219 }, 1220 "resource2": { 1221 { 1222 NUMANodeAffinity: NewTestBitMask(0), 1223 Preferred: true, 1224 }, 1225 { 1226 NUMANodeAffinity: NewTestBitMask(1), 1227 Preferred: true, 1228 }, 1229 { 1230 NUMANodeAffinity: NewTestBitMask(0, 1), 1231 Preferred: false, 1232 }, 1233 }, 1234 }, 1235 }, 1236 }, 1237 expected: TopologyHint{ 1238 NUMANodeAffinity: nil, 1239 Preferred: false, 1240 }, 1241 }, 1242 { 1243 name: "One no-preference provider", 1244 hp: []HintProvider{ 1245 &mockHintProvider{ 1246 map[string][]TopologyHint{ 1247 "resource1": { 1248 { 1249 NUMANodeAffinity: NewTestBitMask(0), 1250 Preferred: true, 1251 }, 1252 { 1253 NUMANodeAffinity: NewTestBitMask(1), 1254 Preferred: true, 1255 }, 1256 { 1257 NUMANodeAffinity: NewTestBitMask(0, 1), 1258 Preferred: false, 1259 }, 1260 }, 1261 }, 1262 }, 1263 &mockHintProvider{ 1264 nil, 1265 }, 1266 }, 1267 expected: TopologyHint{ 1268 NUMANodeAffinity: NewTestBitMask(0), 1269 Preferred: true, 1270 }, 1271 }, 1272 } 1273 } 1274 1275 func testPolicyMerge(policy Policy, tcases []policyMergeTestCase, t *testing.T) { 1276 for _, tc := range tcases { 1277 var providersHints []map[string][]TopologyHint 1278 for _, provider := range tc.hp { 1279 hints := provider.GetTopologyHints(&v1.Pod{}, &v1.Container{}) 1280 providersHints = append(providersHints, hints) 1281 } 1282 1283 actual, _ := policy.Merge(providersHints) 1284 if !reflect.DeepEqual(actual, tc.expected) { 1285 t.Errorf("%v: Expected Topology Hint to be %v, got %v:", tc.name, tc.expected, actual) 1286 } 1287 } 1288 } 1289 1290 func TestMaxOfMinAffinityCounts(t *testing.T) { 1291 tcases := []struct { 1292 hints [][]TopologyHint 1293 expected int 1294 }{ 1295 { 1296 [][]TopologyHint{}, 1297 0, 1298 }, 1299 { 1300 [][]TopologyHint{ 1301 { 1302 TopologyHint{NewTestBitMask(), true}, 1303 }, 1304 }, 1305 0, 1306 }, 1307 { 1308 [][]TopologyHint{ 1309 { 1310 TopologyHint{NewTestBitMask(0), true}, 1311 }, 1312 }, 1313 1, 1314 }, 1315 { 1316 [][]TopologyHint{ 1317 { 1318 TopologyHint{NewTestBitMask(0, 1), true}, 1319 }, 1320 }, 1321 2, 1322 }, 1323 { 1324 [][]TopologyHint{ 1325 { 1326 TopologyHint{NewTestBitMask(0, 1), true}, 1327 TopologyHint{NewTestBitMask(0, 1, 2), true}, 1328 }, 1329 }, 1330 2, 1331 }, 1332 { 1333 [][]TopologyHint{ 1334 { 1335 TopologyHint{NewTestBitMask(0, 1), true}, 1336 TopologyHint{NewTestBitMask(0, 1, 2), true}, 1337 }, 1338 { 1339 TopologyHint{NewTestBitMask(0, 1, 2), true}, 1340 }, 1341 }, 1342 3, 1343 }, 1344 { 1345 [][]TopologyHint{ 1346 { 1347 TopologyHint{NewTestBitMask(0, 1), true}, 1348 TopologyHint{NewTestBitMask(0, 1, 2), true}, 1349 }, 1350 { 1351 TopologyHint{NewTestBitMask(0, 1, 2), true}, 1352 TopologyHint{NewTestBitMask(0, 1, 2, 3), true}, 1353 }, 1354 }, 1355 3, 1356 }, 1357 } 1358 1359 for _, tc := range tcases { 1360 t.Run("", func(t *testing.T) { 1361 result := maxOfMinAffinityCounts(tc.hints) 1362 if result != tc.expected { 1363 t.Errorf("Expected result to be %v, got %v", tc.expected, result) 1364 } 1365 }) 1366 } 1367 } 1368 1369 func TestCompareHintsNarrowest(t *testing.T) { 1370 tcases := []struct { 1371 description string 1372 bestNonPreferredAffinityCount int 1373 current *TopologyHint 1374 candidate *TopologyHint 1375 expected string 1376 }{ 1377 { 1378 "candidate.NUMANodeAffinity.Count() == 0 (1)", 1379 -1, 1380 nil, 1381 &TopologyHint{bitmask.NewEmptyBitMask(), false}, 1382 "current", 1383 }, 1384 { 1385 "candidate.NUMANodeAffinity.Count() == 0 (2)", 1386 -1, 1387 &TopologyHint{NewTestBitMask(), true}, 1388 &TopologyHint{NewTestBitMask(), false}, 1389 "current", 1390 }, 1391 { 1392 "current == nil (1)", 1393 -1, 1394 nil, 1395 &TopologyHint{NewTestBitMask(0), true}, 1396 "candidate", 1397 }, 1398 { 1399 "current == nil (2)", 1400 -1, 1401 nil, 1402 &TopologyHint{NewTestBitMask(0), false}, 1403 "candidate", 1404 }, 1405 { 1406 "!current.Preferred && candidate.Preferred", 1407 -1, 1408 &TopologyHint{NewTestBitMask(0), false}, 1409 &TopologyHint{NewTestBitMask(0), true}, 1410 "candidate", 1411 }, 1412 { 1413 "current.Preferred && !candidate.Preferred", 1414 -1, 1415 &TopologyHint{NewTestBitMask(0), true}, 1416 &TopologyHint{NewTestBitMask(0), false}, 1417 "current", 1418 }, 1419 { 1420 "current.Preferred && candidate.Preferred (1)", 1421 -1, 1422 &TopologyHint{NewTestBitMask(0), true}, 1423 &TopologyHint{NewTestBitMask(0), true}, 1424 "current", 1425 }, 1426 { 1427 "current.Preferred && candidate.Preferred (2)", 1428 -1, 1429 &TopologyHint{NewTestBitMask(0, 1), true}, 1430 &TopologyHint{NewTestBitMask(0), true}, 1431 "candidate", 1432 }, 1433 { 1434 "current.Preferred && candidate.Preferred (3)", 1435 -1, 1436 &TopologyHint{NewTestBitMask(0), true}, 1437 &TopologyHint{NewTestBitMask(0, 1), true}, 1438 "current", 1439 }, 1440 { 1441 "!current.Preferred && !candidate.Preferred (1.1)", 1442 1, 1443 &TopologyHint{NewTestBitMask(0, 1), false}, 1444 &TopologyHint{NewTestBitMask(0, 1), false}, 1445 "current", 1446 }, 1447 { 1448 "!current.Preferred && !candidate.Preferred (1.2)", 1449 1, 1450 &TopologyHint{NewTestBitMask(1, 2), false}, 1451 &TopologyHint{NewTestBitMask(0, 1), false}, 1452 "candidate", 1453 }, 1454 { 1455 "!current.Preferred && !candidate.Preferred (1.3)", 1456 1, 1457 &TopologyHint{NewTestBitMask(0, 1), false}, 1458 &TopologyHint{NewTestBitMask(1, 2), false}, 1459 "current", 1460 }, 1461 { 1462 "!current.Preferred && !candidate.Preferred (2.1)", 1463 2, 1464 &TopologyHint{NewTestBitMask(0, 1), false}, 1465 &TopologyHint{NewTestBitMask(0), false}, 1466 "current", 1467 }, 1468 { 1469 "!current.Preferred && !candidate.Preferred (2.2)", 1470 2, 1471 &TopologyHint{NewTestBitMask(0, 1), false}, 1472 &TopologyHint{NewTestBitMask(0, 1), false}, 1473 "current", 1474 }, 1475 { 1476 "!current.Preferred && !candidate.Preferred (2.3)", 1477 2, 1478 &TopologyHint{NewTestBitMask(1, 2), false}, 1479 &TopologyHint{NewTestBitMask(0, 1), false}, 1480 "candidate", 1481 }, 1482 { 1483 "!current.Preferred && !candidate.Preferred (2.4)", 1484 2, 1485 &TopologyHint{NewTestBitMask(0, 1), false}, 1486 &TopologyHint{NewTestBitMask(1, 2), false}, 1487 "current", 1488 }, 1489 { 1490 "!current.Preferred && !candidate.Preferred (3a)", 1491 2, 1492 &TopologyHint{NewTestBitMask(0), false}, 1493 &TopologyHint{NewTestBitMask(0, 1, 2), false}, 1494 "current", 1495 }, 1496 { 1497 "!current.Preferred && !candidate.Preferred (3b)", 1498 2, 1499 &TopologyHint{NewTestBitMask(0), false}, 1500 &TopologyHint{NewTestBitMask(0, 1), false}, 1501 "candidate", 1502 }, 1503 { 1504 "!current.Preferred && !candidate.Preferred (3ca.1)", 1505 3, 1506 &TopologyHint{NewTestBitMask(0), false}, 1507 &TopologyHint{NewTestBitMask(0, 1), false}, 1508 "candidate", 1509 }, 1510 { 1511 "!current.Preferred && !candidate.Preferred (3ca.2)", 1512 3, 1513 &TopologyHint{NewTestBitMask(0), false}, 1514 &TopologyHint{NewTestBitMask(1, 2), false}, 1515 "candidate", 1516 }, 1517 { 1518 "!current.Preferred && !candidate.Preferred (3ca.3)", 1519 4, 1520 &TopologyHint{NewTestBitMask(0, 1), false}, 1521 &TopologyHint{NewTestBitMask(1, 2, 3), false}, 1522 "candidate", 1523 }, 1524 { 1525 "!current.Preferred && !candidate.Preferred (3cb)", 1526 4, 1527 &TopologyHint{NewTestBitMask(1, 2, 3), false}, 1528 &TopologyHint{NewTestBitMask(0, 1), false}, 1529 "current", 1530 }, 1531 { 1532 "!current.Preferred && !candidate.Preferred (3cc.1)", 1533 4, 1534 &TopologyHint{NewTestBitMask(0, 1, 2), false}, 1535 &TopologyHint{NewTestBitMask(0, 1, 2), false}, 1536 "current", 1537 }, 1538 { 1539 "!current.Preferred && !candidate.Preferred (3cc.2)", 1540 4, 1541 &TopologyHint{NewTestBitMask(0, 1, 2), false}, 1542 &TopologyHint{NewTestBitMask(1, 2, 3), false}, 1543 "current", 1544 }, 1545 { 1546 "!current.Preferred && !candidate.Preferred (3cc.3)", 1547 4, 1548 &TopologyHint{NewTestBitMask(1, 2, 3), false}, 1549 &TopologyHint{NewTestBitMask(0, 1, 2), false}, 1550 "candidate", 1551 }, 1552 } 1553 1554 for _, tc := range tcases { 1555 t.Run(tc.description, func(t *testing.T) { 1556 numaInfo := &NUMAInfo{} 1557 merger := NewHintMerger(numaInfo, [][]TopologyHint{}, PolicyBestEffort, PolicyOptions{}) 1558 merger.BestNonPreferredAffinityCount = tc.bestNonPreferredAffinityCount 1559 1560 result := merger.compare(tc.current, tc.candidate) 1561 if result != tc.current && result != tc.candidate { 1562 t.Errorf("Expected result to be either 'current' or 'candidate' hint") 1563 } 1564 if tc.expected == "current" && result != tc.current { 1565 t.Errorf("Expected result to be %v, got %v", tc.current, result) 1566 } 1567 if tc.expected == "candidate" && result != tc.candidate { 1568 t.Errorf("Expected result to be %v, got %v", tc.candidate, result) 1569 } 1570 }) 1571 } 1572 } 1573 1574 func commonNUMAInfoTwoNodes() *NUMAInfo { 1575 return &NUMAInfo{ 1576 Nodes: []int{0, 1}, 1577 NUMADistances: NUMADistances{ 1578 0: {10, 11}, 1579 1: {11, 10}, 1580 }, 1581 } 1582 } 1583 1584 func commonNUMAInfoFourNodes() *NUMAInfo { 1585 return &NUMAInfo{ 1586 Nodes: []int{0, 1, 2, 3}, 1587 NUMADistances: NUMADistances{ 1588 0: {10, 11, 12, 12}, 1589 1: {11, 10, 12, 12}, 1590 2: {12, 12, 10, 11}, 1591 3: {12, 12, 11, 10}, 1592 }, 1593 } 1594 } 1595 1596 func commonNUMAInfoEightNodes() *NUMAInfo { 1597 return &NUMAInfo{ 1598 Nodes: []int{0, 1, 2, 3, 4, 5, 6, 7}, 1599 NUMADistances: NUMADistances{ 1600 0: {10, 11, 12, 12, 30, 30, 30, 30}, 1601 1: {11, 10, 12, 12, 30, 30, 30, 30}, 1602 2: {12, 12, 10, 11, 30, 30, 30, 30}, 1603 3: {12, 12, 11, 10, 30, 30, 30, 30}, 1604 4: {30, 30, 30, 30, 10, 11, 12, 12}, 1605 5: {30, 30, 30, 30, 11, 10, 12, 12}, 1606 6: {30, 30, 30, 30, 12, 12, 10, 11}, 1607 7: {30, 30, 30, 30, 12, 12, 13, 10}, 1608 }, 1609 } 1610 }