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