k8s.io/kubernetes@v1.29.3/pkg/scheduler/framework/types_test.go (about) 1 /* 2 Copyright 2018 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 framework 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 v1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/api/resource" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/types" 29 "k8s.io/apimachinery/pkg/util/sets" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 "k8s.io/kubernetes/pkg/features" 33 st "k8s.io/kubernetes/pkg/scheduler/testing" 34 "k8s.io/kubernetes/test/utils/ktesting" 35 ) 36 37 func TestNewResource(t *testing.T) { 38 tests := []struct { 39 name string 40 resourceList v1.ResourceList 41 expected *Resource 42 }{ 43 { 44 name: "empty resource", 45 resourceList: map[v1.ResourceName]resource.Quantity{}, 46 expected: &Resource{}, 47 }, 48 { 49 name: "complex resource", 50 resourceList: map[v1.ResourceName]resource.Quantity{ 51 v1.ResourceCPU: *resource.NewScaledQuantity(4, -3), 52 v1.ResourceMemory: *resource.NewQuantity(2000, resource.BinarySI), 53 v1.ResourcePods: *resource.NewQuantity(80, resource.BinarySI), 54 v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI), 55 "scalar.test/" + "scalar1": *resource.NewQuantity(1, resource.DecimalSI), 56 v1.ResourceHugePagesPrefix + "test": *resource.NewQuantity(2, resource.BinarySI), 57 }, 58 expected: &Resource{ 59 MilliCPU: 4, 60 Memory: 2000, 61 EphemeralStorage: 5000, 62 AllowedPodNumber: 80, 63 ScalarResources: map[v1.ResourceName]int64{"scalar.test/scalar1": 1, "hugepages-test": 2}, 64 }, 65 }, 66 } 67 68 for _, test := range tests { 69 t.Run(test.name, func(t *testing.T) { 70 r := NewResource(test.resourceList) 71 if !reflect.DeepEqual(test.expected, r) { 72 t.Errorf("expected: %#v, got: %#v", test.expected, r) 73 } 74 }) 75 } 76 } 77 78 func TestResourceClone(t *testing.T) { 79 tests := []struct { 80 resource *Resource 81 expected *Resource 82 }{ 83 { 84 resource: &Resource{}, 85 expected: &Resource{}, 86 }, 87 { 88 resource: &Resource{ 89 MilliCPU: 4, 90 Memory: 2000, 91 EphemeralStorage: 5000, 92 AllowedPodNumber: 80, 93 ScalarResources: map[v1.ResourceName]int64{"scalar.test/scalar1": 1, "hugepages-test": 2}, 94 }, 95 expected: &Resource{ 96 MilliCPU: 4, 97 Memory: 2000, 98 EphemeralStorage: 5000, 99 AllowedPodNumber: 80, 100 ScalarResources: map[v1.ResourceName]int64{"scalar.test/scalar1": 1, "hugepages-test": 2}, 101 }, 102 }, 103 } 104 105 for i, test := range tests { 106 t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { 107 r := test.resource.Clone() 108 // Modify the field to check if the result is a clone of the origin one. 109 test.resource.MilliCPU += 1000 110 if !reflect.DeepEqual(test.expected, r) { 111 t.Errorf("expected: %#v, got: %#v", test.expected, r) 112 } 113 }) 114 } 115 } 116 117 func TestResourceAddScalar(t *testing.T) { 118 tests := []struct { 119 resource *Resource 120 scalarName v1.ResourceName 121 scalarQuantity int64 122 expected *Resource 123 }{ 124 { 125 resource: &Resource{}, 126 scalarName: "scalar1", 127 scalarQuantity: 100, 128 expected: &Resource{ 129 ScalarResources: map[v1.ResourceName]int64{"scalar1": 100}, 130 }, 131 }, 132 { 133 resource: &Resource{ 134 MilliCPU: 4, 135 Memory: 2000, 136 EphemeralStorage: 5000, 137 AllowedPodNumber: 80, 138 ScalarResources: map[v1.ResourceName]int64{"hugepages-test": 2}, 139 }, 140 scalarName: "scalar2", 141 scalarQuantity: 200, 142 expected: &Resource{ 143 MilliCPU: 4, 144 Memory: 2000, 145 EphemeralStorage: 5000, 146 AllowedPodNumber: 80, 147 ScalarResources: map[v1.ResourceName]int64{"hugepages-test": 2, "scalar2": 200}, 148 }, 149 }, 150 } 151 152 for _, test := range tests { 153 t.Run(string(test.scalarName), func(t *testing.T) { 154 test.resource.AddScalar(test.scalarName, test.scalarQuantity) 155 if !reflect.DeepEqual(test.expected, test.resource) { 156 t.Errorf("expected: %#v, got: %#v", test.expected, test.resource) 157 } 158 }) 159 } 160 } 161 162 func TestSetMaxResource(t *testing.T) { 163 tests := []struct { 164 resource *Resource 165 resourceList v1.ResourceList 166 expected *Resource 167 }{ 168 { 169 resource: &Resource{}, 170 resourceList: map[v1.ResourceName]resource.Quantity{ 171 v1.ResourceCPU: *resource.NewScaledQuantity(4, -3), 172 v1.ResourceMemory: *resource.NewQuantity(2000, resource.BinarySI), 173 v1.ResourceEphemeralStorage: *resource.NewQuantity(5000, resource.BinarySI), 174 }, 175 expected: &Resource{ 176 MilliCPU: 4, 177 Memory: 2000, 178 EphemeralStorage: 5000, 179 }, 180 }, 181 { 182 resource: &Resource{ 183 MilliCPU: 4, 184 Memory: 4000, 185 EphemeralStorage: 5000, 186 ScalarResources: map[v1.ResourceName]int64{"scalar.test/scalar1": 1, "hugepages-test": 2}, 187 }, 188 resourceList: map[v1.ResourceName]resource.Quantity{ 189 v1.ResourceCPU: *resource.NewScaledQuantity(4, -3), 190 v1.ResourceMemory: *resource.NewQuantity(2000, resource.BinarySI), 191 v1.ResourceEphemeralStorage: *resource.NewQuantity(7000, resource.BinarySI), 192 "scalar.test/scalar1": *resource.NewQuantity(4, resource.DecimalSI), 193 v1.ResourceHugePagesPrefix + "test": *resource.NewQuantity(5, resource.BinarySI), 194 }, 195 expected: &Resource{ 196 MilliCPU: 4, 197 Memory: 4000, 198 EphemeralStorage: 7000, 199 ScalarResources: map[v1.ResourceName]int64{"scalar.test/scalar1": 4, "hugepages-test": 5}, 200 }, 201 }, 202 } 203 204 for i, test := range tests { 205 t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { 206 test.resource.SetMaxResource(test.resourceList) 207 if !reflect.DeepEqual(test.expected, test.resource) { 208 t.Errorf("expected: %#v, got: %#v", test.expected, test.resource) 209 } 210 }) 211 } 212 } 213 214 func TestNewNodeInfo(t *testing.T) { 215 nodeName := "test-node" 216 pods := []*v1.Pod{ 217 st.MakePod().UID("test-1").Namespace("node_info_cache_test").Name("test-1").Node(nodeName). 218 Containers([]v1.Container{st.MakeContainer().ResourceRequests(map[v1.ResourceName]string{ 219 v1.ResourceCPU: "100m", 220 v1.ResourceMemory: "500", 221 }).ContainerPort([]v1.ContainerPort{{ 222 HostIP: "127.0.0.1", 223 HostPort: 80, 224 Protocol: "TCP", 225 }}).Obj()}). 226 Obj(), 227 228 st.MakePod().UID("test-2").Namespace("node_info_cache_test").Name("test-2").Node(nodeName). 229 Containers([]v1.Container{st.MakeContainer().ResourceRequests(map[v1.ResourceName]string{ 230 v1.ResourceCPU: "200m", 231 v1.ResourceMemory: "1Ki", 232 }).ContainerPort([]v1.ContainerPort{{ 233 HostIP: "127.0.0.1", 234 HostPort: 8080, 235 Protocol: "TCP", 236 }}).Obj()}). 237 Obj(), 238 } 239 240 expected := &NodeInfo{ 241 Requested: &Resource{ 242 MilliCPU: 300, 243 Memory: 1524, 244 EphemeralStorage: 0, 245 AllowedPodNumber: 0, 246 ScalarResources: map[v1.ResourceName]int64(nil), 247 }, 248 NonZeroRequested: &Resource{ 249 MilliCPU: 300, 250 Memory: 1524, 251 EphemeralStorage: 0, 252 AllowedPodNumber: 0, 253 ScalarResources: map[v1.ResourceName]int64(nil), 254 }, 255 Allocatable: &Resource{}, 256 Generation: 2, 257 UsedPorts: HostPortInfo{ 258 "127.0.0.1": map[ProtocolPort]struct{}{ 259 {Protocol: "TCP", Port: 80}: {}, 260 {Protocol: "TCP", Port: 8080}: {}, 261 }, 262 }, 263 ImageStates: map[string]*ImageStateSummary{}, 264 PVCRefCounts: map[string]int{}, 265 Pods: []*PodInfo{ 266 { 267 Pod: &v1.Pod{ 268 ObjectMeta: metav1.ObjectMeta{ 269 Namespace: "node_info_cache_test", 270 Name: "test-1", 271 UID: types.UID("test-1"), 272 }, 273 Spec: v1.PodSpec{ 274 Containers: []v1.Container{ 275 { 276 Resources: v1.ResourceRequirements{ 277 Requests: v1.ResourceList{ 278 v1.ResourceCPU: resource.MustParse("100m"), 279 v1.ResourceMemory: resource.MustParse("500"), 280 }, 281 }, 282 Ports: []v1.ContainerPort{ 283 { 284 HostIP: "127.0.0.1", 285 HostPort: 80, 286 Protocol: "TCP", 287 }, 288 }, 289 }, 290 }, 291 NodeName: nodeName, 292 }, 293 }, 294 }, 295 { 296 Pod: &v1.Pod{ 297 ObjectMeta: metav1.ObjectMeta{ 298 Namespace: "node_info_cache_test", 299 Name: "test-2", 300 UID: types.UID("test-2"), 301 }, 302 Spec: v1.PodSpec{ 303 Containers: []v1.Container{ 304 { 305 Resources: v1.ResourceRequirements{ 306 Requests: v1.ResourceList{ 307 v1.ResourceCPU: resource.MustParse("200m"), 308 v1.ResourceMemory: resource.MustParse("1Ki"), 309 }, 310 }, 311 Ports: []v1.ContainerPort{ 312 { 313 HostIP: "127.0.0.1", 314 HostPort: 8080, 315 Protocol: "TCP", 316 }, 317 }, 318 }, 319 }, 320 NodeName: nodeName, 321 }, 322 }, 323 }, 324 }, 325 } 326 327 gen := generation 328 ni := NewNodeInfo(pods...) 329 if ni.Generation <= gen { 330 t.Errorf("Generation is not incremented. previous: %v, current: %v", gen, ni.Generation) 331 } 332 expected.Generation = ni.Generation 333 if !reflect.DeepEqual(expected, ni) { 334 t.Errorf("expected: %#v, got: %#v", expected, ni) 335 } 336 } 337 338 func TestNodeInfoClone(t *testing.T) { 339 nodeName := "test-node" 340 tests := []struct { 341 nodeInfo *NodeInfo 342 expected *NodeInfo 343 }{ 344 { 345 nodeInfo: &NodeInfo{ 346 Requested: &Resource{}, 347 NonZeroRequested: &Resource{}, 348 Allocatable: &Resource{}, 349 Generation: 2, 350 UsedPorts: HostPortInfo{ 351 "127.0.0.1": map[ProtocolPort]struct{}{ 352 {Protocol: "TCP", Port: 80}: {}, 353 {Protocol: "TCP", Port: 8080}: {}, 354 }, 355 }, 356 ImageStates: map[string]*ImageStateSummary{}, 357 PVCRefCounts: map[string]int{}, 358 Pods: []*PodInfo{ 359 { 360 Pod: &v1.Pod{ 361 ObjectMeta: metav1.ObjectMeta{ 362 Namespace: "node_info_cache_test", 363 Name: "test-1", 364 UID: types.UID("test-1"), 365 }, 366 Spec: v1.PodSpec{ 367 Containers: []v1.Container{ 368 { 369 Resources: v1.ResourceRequirements{ 370 Requests: v1.ResourceList{ 371 v1.ResourceCPU: resource.MustParse("100m"), 372 v1.ResourceMemory: resource.MustParse("500"), 373 }, 374 }, 375 Ports: []v1.ContainerPort{ 376 { 377 HostIP: "127.0.0.1", 378 HostPort: 80, 379 Protocol: "TCP", 380 }, 381 }, 382 }, 383 }, 384 NodeName: nodeName, 385 }, 386 }, 387 }, 388 { 389 Pod: &v1.Pod{ 390 ObjectMeta: metav1.ObjectMeta{ 391 Namespace: "node_info_cache_test", 392 Name: "test-2", 393 UID: types.UID("test-2"), 394 }, 395 Spec: v1.PodSpec{ 396 Containers: []v1.Container{ 397 { 398 Resources: v1.ResourceRequirements{ 399 Requests: v1.ResourceList{ 400 v1.ResourceCPU: resource.MustParse("200m"), 401 v1.ResourceMemory: resource.MustParse("1Ki"), 402 }, 403 }, 404 Ports: []v1.ContainerPort{ 405 { 406 HostIP: "127.0.0.1", 407 HostPort: 8080, 408 Protocol: "TCP", 409 }, 410 }, 411 }, 412 }, 413 NodeName: nodeName, 414 }, 415 }, 416 }, 417 }, 418 }, 419 expected: &NodeInfo{ 420 Requested: &Resource{}, 421 NonZeroRequested: &Resource{}, 422 Allocatable: &Resource{}, 423 Generation: 2, 424 UsedPorts: HostPortInfo{ 425 "127.0.0.1": map[ProtocolPort]struct{}{ 426 {Protocol: "TCP", Port: 80}: {}, 427 {Protocol: "TCP", Port: 8080}: {}, 428 }, 429 }, 430 ImageStates: map[string]*ImageStateSummary{}, 431 PVCRefCounts: map[string]int{}, 432 Pods: []*PodInfo{ 433 { 434 Pod: &v1.Pod{ 435 ObjectMeta: metav1.ObjectMeta{ 436 Namespace: "node_info_cache_test", 437 Name: "test-1", 438 UID: types.UID("test-1"), 439 }, 440 Spec: v1.PodSpec{ 441 Containers: []v1.Container{ 442 { 443 Resources: v1.ResourceRequirements{ 444 Requests: v1.ResourceList{ 445 v1.ResourceCPU: resource.MustParse("100m"), 446 v1.ResourceMemory: resource.MustParse("500"), 447 }, 448 }, 449 Ports: []v1.ContainerPort{ 450 { 451 HostIP: "127.0.0.1", 452 HostPort: 80, 453 Protocol: "TCP", 454 }, 455 }, 456 }, 457 }, 458 NodeName: nodeName, 459 }, 460 }, 461 }, 462 { 463 Pod: &v1.Pod{ 464 ObjectMeta: metav1.ObjectMeta{ 465 Namespace: "node_info_cache_test", 466 Name: "test-2", 467 UID: types.UID("test-2"), 468 }, 469 Spec: v1.PodSpec{ 470 Containers: []v1.Container{ 471 { 472 Resources: v1.ResourceRequirements{ 473 Requests: v1.ResourceList{ 474 v1.ResourceCPU: resource.MustParse("200m"), 475 v1.ResourceMemory: resource.MustParse("1Ki"), 476 }, 477 }, 478 Ports: []v1.ContainerPort{ 479 { 480 HostIP: "127.0.0.1", 481 HostPort: 8080, 482 Protocol: "TCP", 483 }, 484 }, 485 }, 486 }, 487 NodeName: nodeName, 488 }, 489 }, 490 }, 491 }, 492 }, 493 }, 494 } 495 496 for i, test := range tests { 497 t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { 498 ni := test.nodeInfo.Snapshot() 499 // Modify the field to check if the result is a clone of the origin one. 500 test.nodeInfo.Generation += 10 501 test.nodeInfo.UsedPorts.Remove("127.0.0.1", "TCP", 80) 502 if !reflect.DeepEqual(test.expected, ni) { 503 t.Errorf("expected: %#v, got: %#v", test.expected, ni) 504 } 505 }) 506 } 507 } 508 509 func TestNodeInfoAddPod(t *testing.T) { 510 nodeName := "test-node" 511 pods := []*v1.Pod{ 512 { 513 ObjectMeta: metav1.ObjectMeta{ 514 Namespace: "node_info_cache_test", 515 Name: "test-1", 516 UID: types.UID("test-1"), 517 }, 518 Spec: v1.PodSpec{ 519 Containers: []v1.Container{ 520 { 521 Resources: v1.ResourceRequirements{ 522 Requests: v1.ResourceList{ 523 v1.ResourceCPU: resource.MustParse("100m"), 524 v1.ResourceMemory: resource.MustParse("500"), 525 }, 526 }, 527 Ports: []v1.ContainerPort{ 528 { 529 HostIP: "127.0.0.1", 530 HostPort: 80, 531 Protocol: "TCP", 532 }, 533 }, 534 }, 535 }, 536 NodeName: nodeName, 537 Overhead: v1.ResourceList{ 538 v1.ResourceCPU: resource.MustParse("500m"), 539 }, 540 Volumes: []v1.Volume{ 541 { 542 VolumeSource: v1.VolumeSource{ 543 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 544 ClaimName: "pvc-1", 545 }, 546 }, 547 }, 548 }, 549 }, 550 }, 551 { 552 ObjectMeta: metav1.ObjectMeta{ 553 Namespace: "node_info_cache_test", 554 Name: "test-2", 555 UID: types.UID("test-2"), 556 }, 557 Spec: v1.PodSpec{ 558 Containers: []v1.Container{ 559 { 560 Resources: v1.ResourceRequirements{ 561 Requests: v1.ResourceList{ 562 v1.ResourceCPU: resource.MustParse("200m"), 563 }, 564 }, 565 Ports: []v1.ContainerPort{ 566 { 567 HostIP: "127.0.0.1", 568 HostPort: 8080, 569 Protocol: "TCP", 570 }, 571 }, 572 }, 573 }, 574 NodeName: nodeName, 575 Overhead: v1.ResourceList{ 576 v1.ResourceCPU: resource.MustParse("500m"), 577 v1.ResourceMemory: resource.MustParse("500"), 578 }, 579 Volumes: []v1.Volume{ 580 { 581 VolumeSource: v1.VolumeSource{ 582 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 583 ClaimName: "pvc-1", 584 }, 585 }, 586 }, 587 }, 588 }, 589 }, 590 { 591 ObjectMeta: metav1.ObjectMeta{ 592 Namespace: "node_info_cache_test", 593 Name: "test-3", 594 UID: types.UID("test-3"), 595 }, 596 Spec: v1.PodSpec{ 597 Containers: []v1.Container{ 598 { 599 Resources: v1.ResourceRequirements{ 600 Requests: v1.ResourceList{ 601 v1.ResourceCPU: resource.MustParse("200m"), 602 }, 603 }, 604 Ports: []v1.ContainerPort{ 605 { 606 HostIP: "127.0.0.1", 607 HostPort: 8080, 608 Protocol: "TCP", 609 }, 610 }, 611 }, 612 }, 613 InitContainers: []v1.Container{ 614 { 615 Resources: v1.ResourceRequirements{ 616 Requests: v1.ResourceList{ 617 v1.ResourceCPU: resource.MustParse("500m"), 618 v1.ResourceMemory: resource.MustParse("200Mi"), 619 }, 620 }, 621 }, 622 }, 623 NodeName: nodeName, 624 Overhead: v1.ResourceList{ 625 v1.ResourceCPU: resource.MustParse("500m"), 626 v1.ResourceMemory: resource.MustParse("500"), 627 }, 628 Volumes: []v1.Volume{ 629 { 630 VolumeSource: v1.VolumeSource{ 631 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 632 ClaimName: "pvc-2", 633 }, 634 }, 635 }, 636 }, 637 }, 638 }, 639 } 640 expected := &NodeInfo{ 641 node: &v1.Node{ 642 ObjectMeta: metav1.ObjectMeta{ 643 Name: "test-node", 644 }, 645 }, 646 Requested: &Resource{ 647 MilliCPU: 2300, 648 Memory: 209716700, //1500 + 200MB in initContainers 649 EphemeralStorage: 0, 650 AllowedPodNumber: 0, 651 ScalarResources: map[v1.ResourceName]int64(nil), 652 }, 653 NonZeroRequested: &Resource{ 654 MilliCPU: 2300, 655 Memory: 419431900, //200MB(initContainers) + 200MB(default memory value) + 1500 specified in requests/overhead 656 EphemeralStorage: 0, 657 AllowedPodNumber: 0, 658 ScalarResources: map[v1.ResourceName]int64(nil), 659 }, 660 Allocatable: &Resource{}, 661 Generation: 2, 662 UsedPorts: HostPortInfo{ 663 "127.0.0.1": map[ProtocolPort]struct{}{ 664 {Protocol: "TCP", Port: 80}: {}, 665 {Protocol: "TCP", Port: 8080}: {}, 666 }, 667 }, 668 ImageStates: map[string]*ImageStateSummary{}, 669 PVCRefCounts: map[string]int{"node_info_cache_test/pvc-1": 2, "node_info_cache_test/pvc-2": 1}, 670 Pods: []*PodInfo{ 671 { 672 Pod: &v1.Pod{ 673 ObjectMeta: metav1.ObjectMeta{ 674 Namespace: "node_info_cache_test", 675 Name: "test-1", 676 UID: types.UID("test-1"), 677 }, 678 Spec: v1.PodSpec{ 679 Containers: []v1.Container{ 680 { 681 Resources: v1.ResourceRequirements{ 682 Requests: v1.ResourceList{ 683 v1.ResourceCPU: resource.MustParse("100m"), 684 v1.ResourceMemory: resource.MustParse("500"), 685 }, 686 }, 687 Ports: []v1.ContainerPort{ 688 { 689 HostIP: "127.0.0.1", 690 HostPort: 80, 691 Protocol: "TCP", 692 }, 693 }, 694 }, 695 }, 696 NodeName: nodeName, 697 Overhead: v1.ResourceList{ 698 v1.ResourceCPU: resource.MustParse("500m"), 699 }, 700 Volumes: []v1.Volume{ 701 { 702 VolumeSource: v1.VolumeSource{ 703 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 704 ClaimName: "pvc-1", 705 }, 706 }, 707 }, 708 }, 709 }, 710 }, 711 }, 712 { 713 Pod: &v1.Pod{ 714 ObjectMeta: metav1.ObjectMeta{ 715 Namespace: "node_info_cache_test", 716 Name: "test-2", 717 UID: types.UID("test-2"), 718 }, 719 Spec: v1.PodSpec{ 720 Containers: []v1.Container{ 721 { 722 Resources: v1.ResourceRequirements{ 723 Requests: v1.ResourceList{ 724 v1.ResourceCPU: resource.MustParse("200m"), 725 }, 726 }, 727 Ports: []v1.ContainerPort{ 728 { 729 HostIP: "127.0.0.1", 730 HostPort: 8080, 731 Protocol: "TCP", 732 }, 733 }, 734 }, 735 }, 736 NodeName: nodeName, 737 Overhead: v1.ResourceList{ 738 v1.ResourceCPU: resource.MustParse("500m"), 739 v1.ResourceMemory: resource.MustParse("500"), 740 }, 741 Volumes: []v1.Volume{ 742 { 743 VolumeSource: v1.VolumeSource{ 744 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 745 ClaimName: "pvc-1", 746 }, 747 }, 748 }, 749 }, 750 }, 751 }, 752 }, 753 { 754 Pod: &v1.Pod{ 755 ObjectMeta: metav1.ObjectMeta{ 756 Namespace: "node_info_cache_test", 757 Name: "test-3", 758 UID: types.UID("test-3"), 759 }, 760 Spec: v1.PodSpec{ 761 Containers: []v1.Container{ 762 { 763 Resources: v1.ResourceRequirements{ 764 Requests: v1.ResourceList{ 765 v1.ResourceCPU: resource.MustParse("200m"), 766 }, 767 }, 768 Ports: []v1.ContainerPort{ 769 { 770 HostIP: "127.0.0.1", 771 HostPort: 8080, 772 Protocol: "TCP", 773 }, 774 }, 775 }, 776 }, 777 InitContainers: []v1.Container{ 778 { 779 Resources: v1.ResourceRequirements{ 780 Requests: v1.ResourceList{ 781 v1.ResourceCPU: resource.MustParse("500m"), 782 v1.ResourceMemory: resource.MustParse("200Mi"), 783 }, 784 }, 785 }, 786 }, 787 NodeName: nodeName, 788 Overhead: v1.ResourceList{ 789 v1.ResourceCPU: resource.MustParse("500m"), 790 v1.ResourceMemory: resource.MustParse("500"), 791 }, 792 Volumes: []v1.Volume{ 793 { 794 VolumeSource: v1.VolumeSource{ 795 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 796 ClaimName: "pvc-2", 797 }, 798 }, 799 }, 800 }, 801 }, 802 }, 803 }, 804 }, 805 } 806 807 ni := fakeNodeInfo() 808 gen := ni.Generation 809 for _, pod := range pods { 810 ni.AddPod(pod) 811 if ni.Generation <= gen { 812 t.Errorf("Generation is not incremented. Prev: %v, current: %v", gen, ni.Generation) 813 } 814 gen = ni.Generation 815 } 816 817 expected.Generation = ni.Generation 818 if !reflect.DeepEqual(expected, ni) { 819 t.Errorf("expected: %#v, got: %#v", expected, ni) 820 } 821 } 822 823 func TestNodeInfoRemovePod(t *testing.T) { 824 nodeName := "test-node" 825 pods := []*v1.Pod{ 826 st.MakePod().UID("test-1").Namespace("node_info_cache_test").Name("test-1").Node(nodeName). 827 Containers([]v1.Container{st.MakeContainer().ResourceRequests(map[v1.ResourceName]string{ 828 v1.ResourceCPU: "100m", 829 v1.ResourceMemory: "500", 830 }).ContainerPort([]v1.ContainerPort{{ 831 HostIP: "127.0.0.1", 832 HostPort: 80, 833 Protocol: "TCP", 834 }}).Obj()}). 835 Volumes([]v1.Volume{{VolumeSource: v1.VolumeSource{PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: "pvc-1"}}}}). 836 Obj(), 837 838 st.MakePod().UID("test-2").Namespace("node_info_cache_test").Name("test-2").Node(nodeName). 839 Containers([]v1.Container{st.MakeContainer().ResourceRequests(map[v1.ResourceName]string{ 840 v1.ResourceCPU: "200m", 841 v1.ResourceMemory: "1Ki", 842 }).ContainerPort([]v1.ContainerPort{{ 843 HostIP: "127.0.0.1", 844 HostPort: 8080, 845 Protocol: "TCP", 846 }}).Obj()}). 847 Obj(), 848 } 849 850 // add pod Overhead 851 for _, pod := range pods { 852 pod.Spec.Overhead = v1.ResourceList{ 853 v1.ResourceCPU: resource.MustParse("500m"), 854 v1.ResourceMemory: resource.MustParse("500"), 855 } 856 } 857 858 tests := []struct { 859 pod *v1.Pod 860 errExpected bool 861 expectedNodeInfo *NodeInfo 862 }{ 863 { 864 pod: st.MakePod().UID("non-exist").Namespace("node_info_cache_test").Node(nodeName).Obj(), 865 errExpected: true, 866 expectedNodeInfo: &NodeInfo{ 867 node: &v1.Node{ 868 ObjectMeta: metav1.ObjectMeta{ 869 Name: "test-node", 870 }, 871 }, 872 Requested: &Resource{ 873 MilliCPU: 1300, 874 Memory: 2524, 875 EphemeralStorage: 0, 876 AllowedPodNumber: 0, 877 ScalarResources: map[v1.ResourceName]int64(nil), 878 }, 879 NonZeroRequested: &Resource{ 880 MilliCPU: 1300, 881 Memory: 2524, 882 EphemeralStorage: 0, 883 AllowedPodNumber: 0, 884 ScalarResources: map[v1.ResourceName]int64(nil), 885 }, 886 Allocatable: &Resource{}, 887 Generation: 2, 888 UsedPorts: HostPortInfo{ 889 "127.0.0.1": map[ProtocolPort]struct{}{ 890 {Protocol: "TCP", Port: 80}: {}, 891 {Protocol: "TCP", Port: 8080}: {}, 892 }, 893 }, 894 ImageStates: map[string]*ImageStateSummary{}, 895 PVCRefCounts: map[string]int{"node_info_cache_test/pvc-1": 1}, 896 Pods: []*PodInfo{ 897 { 898 Pod: &v1.Pod{ 899 ObjectMeta: metav1.ObjectMeta{ 900 Namespace: "node_info_cache_test", 901 Name: "test-1", 902 UID: types.UID("test-1"), 903 }, 904 Spec: v1.PodSpec{ 905 Containers: []v1.Container{ 906 { 907 Resources: v1.ResourceRequirements{ 908 Requests: v1.ResourceList{ 909 v1.ResourceCPU: resource.MustParse("100m"), 910 v1.ResourceMemory: resource.MustParse("500"), 911 }, 912 }, 913 Ports: []v1.ContainerPort{ 914 { 915 HostIP: "127.0.0.1", 916 HostPort: 80, 917 Protocol: "TCP", 918 }, 919 }, 920 }, 921 }, 922 NodeName: nodeName, 923 Overhead: v1.ResourceList{ 924 v1.ResourceCPU: resource.MustParse("500m"), 925 v1.ResourceMemory: resource.MustParse("500"), 926 }, 927 Volumes: []v1.Volume{ 928 { 929 VolumeSource: v1.VolumeSource{ 930 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 931 ClaimName: "pvc-1", 932 }, 933 }, 934 }, 935 }, 936 }, 937 }, 938 }, 939 { 940 Pod: &v1.Pod{ 941 ObjectMeta: metav1.ObjectMeta{ 942 Namespace: "node_info_cache_test", 943 Name: "test-2", 944 UID: types.UID("test-2"), 945 }, 946 Spec: v1.PodSpec{ 947 Containers: []v1.Container{ 948 { 949 Resources: v1.ResourceRequirements{ 950 Requests: v1.ResourceList{ 951 v1.ResourceCPU: resource.MustParse("200m"), 952 v1.ResourceMemory: resource.MustParse("1Ki"), 953 }, 954 }, 955 Ports: []v1.ContainerPort{ 956 { 957 HostIP: "127.0.0.1", 958 HostPort: 8080, 959 Protocol: "TCP", 960 }, 961 }, 962 }, 963 }, 964 NodeName: nodeName, 965 Overhead: v1.ResourceList{ 966 v1.ResourceCPU: resource.MustParse("500m"), 967 v1.ResourceMemory: resource.MustParse("500"), 968 }, 969 }, 970 }, 971 }, 972 }, 973 }, 974 }, 975 { 976 pod: &v1.Pod{ 977 ObjectMeta: metav1.ObjectMeta{ 978 Namespace: "node_info_cache_test", 979 Name: "test-1", 980 UID: types.UID("test-1"), 981 }, 982 Spec: v1.PodSpec{ 983 Containers: []v1.Container{ 984 { 985 Resources: v1.ResourceRequirements{ 986 Requests: v1.ResourceList{ 987 v1.ResourceCPU: resource.MustParse("100m"), 988 v1.ResourceMemory: resource.MustParse("500"), 989 }, 990 }, 991 Ports: []v1.ContainerPort{ 992 { 993 HostIP: "127.0.0.1", 994 HostPort: 80, 995 Protocol: "TCP", 996 }, 997 }, 998 }, 999 }, 1000 NodeName: nodeName, 1001 Overhead: v1.ResourceList{ 1002 v1.ResourceCPU: resource.MustParse("500m"), 1003 v1.ResourceMemory: resource.MustParse("500"), 1004 }, 1005 Volumes: []v1.Volume{ 1006 { 1007 VolumeSource: v1.VolumeSource{ 1008 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ 1009 ClaimName: "pvc-1", 1010 }, 1011 }, 1012 }, 1013 }, 1014 }, 1015 }, 1016 errExpected: false, 1017 expectedNodeInfo: &NodeInfo{ 1018 node: &v1.Node{ 1019 ObjectMeta: metav1.ObjectMeta{ 1020 Name: "test-node", 1021 }, 1022 }, 1023 Requested: &Resource{ 1024 MilliCPU: 700, 1025 Memory: 1524, 1026 EphemeralStorage: 0, 1027 AllowedPodNumber: 0, 1028 ScalarResources: map[v1.ResourceName]int64(nil), 1029 }, 1030 NonZeroRequested: &Resource{ 1031 MilliCPU: 700, 1032 Memory: 1524, 1033 EphemeralStorage: 0, 1034 AllowedPodNumber: 0, 1035 ScalarResources: map[v1.ResourceName]int64(nil), 1036 }, 1037 Allocatable: &Resource{}, 1038 Generation: 3, 1039 UsedPorts: HostPortInfo{ 1040 "127.0.0.1": map[ProtocolPort]struct{}{ 1041 {Protocol: "TCP", Port: 8080}: {}, 1042 }, 1043 }, 1044 ImageStates: map[string]*ImageStateSummary{}, 1045 PVCRefCounts: map[string]int{}, 1046 Pods: []*PodInfo{ 1047 { 1048 Pod: &v1.Pod{ 1049 ObjectMeta: metav1.ObjectMeta{ 1050 Namespace: "node_info_cache_test", 1051 Name: "test-2", 1052 UID: types.UID("test-2"), 1053 }, 1054 Spec: v1.PodSpec{ 1055 Containers: []v1.Container{ 1056 { 1057 Resources: v1.ResourceRequirements{ 1058 Requests: v1.ResourceList{ 1059 v1.ResourceCPU: resource.MustParse("200m"), 1060 v1.ResourceMemory: resource.MustParse("1Ki"), 1061 }, 1062 }, 1063 Ports: []v1.ContainerPort{ 1064 { 1065 HostIP: "127.0.0.1", 1066 HostPort: 8080, 1067 Protocol: "TCP", 1068 }, 1069 }, 1070 }, 1071 }, 1072 NodeName: nodeName, 1073 Overhead: v1.ResourceList{ 1074 v1.ResourceCPU: resource.MustParse("500m"), 1075 v1.ResourceMemory: resource.MustParse("500"), 1076 }, 1077 }, 1078 }, 1079 }, 1080 }, 1081 }, 1082 }, 1083 } 1084 1085 for i, test := range tests { 1086 t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { 1087 logger, _ := ktesting.NewTestContext(t) 1088 ni := fakeNodeInfo(pods...) 1089 1090 gen := ni.Generation 1091 err := ni.RemovePod(logger, test.pod) 1092 if err != nil { 1093 if test.errExpected { 1094 expectedErrorMsg := fmt.Errorf("no corresponding pod %s in pods of node %s", test.pod.Name, ni.Node().Name) 1095 if expectedErrorMsg == err { 1096 t.Errorf("expected error: %v, got: %v", expectedErrorMsg, err) 1097 } 1098 } else { 1099 t.Errorf("expected no error, got: %v", err) 1100 } 1101 } else { 1102 if ni.Generation <= gen { 1103 t.Errorf("Generation is not incremented. Prev: %v, current: %v", gen, ni.Generation) 1104 } 1105 } 1106 1107 test.expectedNodeInfo.Generation = ni.Generation 1108 if !reflect.DeepEqual(test.expectedNodeInfo, ni) { 1109 t.Errorf("expected: %#v, got: %#v", test.expectedNodeInfo, ni) 1110 } 1111 }) 1112 } 1113 } 1114 1115 func fakeNodeInfo(pods ...*v1.Pod) *NodeInfo { 1116 ni := NewNodeInfo(pods...) 1117 ni.SetNode(&v1.Node{ 1118 ObjectMeta: metav1.ObjectMeta{ 1119 Name: "test-node", 1120 }, 1121 }) 1122 return ni 1123 } 1124 1125 type hostPortInfoParam struct { 1126 protocol, ip string 1127 port int32 1128 } 1129 1130 func TestHostPortInfo_AddRemove(t *testing.T) { 1131 tests := []struct { 1132 desc string 1133 added []hostPortInfoParam 1134 removed []hostPortInfoParam 1135 length int 1136 }{ 1137 { 1138 desc: "normal add case", 1139 added: []hostPortInfoParam{ 1140 {"TCP", "127.0.0.1", 79}, 1141 {"UDP", "127.0.0.1", 80}, 1142 {"TCP", "127.0.0.1", 81}, 1143 {"TCP", "127.0.0.1", 82}, 1144 // this might not make sense in real case, but the struct doesn't forbid it. 1145 {"TCP", "0.0.0.0", 79}, 1146 {"UDP", "0.0.0.0", 80}, 1147 {"TCP", "0.0.0.0", 81}, 1148 {"TCP", "0.0.0.0", 82}, 1149 {"TCP", "0.0.0.0", 0}, 1150 {"TCP", "0.0.0.0", -1}, 1151 }, 1152 length: 8, 1153 }, 1154 { 1155 desc: "empty ip and protocol add should work", 1156 added: []hostPortInfoParam{ 1157 {"", "127.0.0.1", 79}, 1158 {"UDP", "127.0.0.1", 80}, 1159 {"", "127.0.0.1", 81}, 1160 {"", "127.0.0.1", 82}, 1161 {"", "", 79}, 1162 {"UDP", "", 80}, 1163 {"", "", 81}, 1164 {"", "", 82}, 1165 {"", "", 0}, 1166 {"", "", -1}, 1167 }, 1168 length: 8, 1169 }, 1170 { 1171 desc: "normal remove case", 1172 added: []hostPortInfoParam{ 1173 {"TCP", "127.0.0.1", 79}, 1174 {"UDP", "127.0.0.1", 80}, 1175 {"TCP", "127.0.0.1", 81}, 1176 {"TCP", "127.0.0.1", 82}, 1177 {"TCP", "0.0.0.0", 79}, 1178 {"UDP", "0.0.0.0", 80}, 1179 {"TCP", "0.0.0.0", 81}, 1180 {"TCP", "0.0.0.0", 82}, 1181 }, 1182 removed: []hostPortInfoParam{ 1183 {"TCP", "127.0.0.1", 79}, 1184 {"UDP", "127.0.0.1", 80}, 1185 {"TCP", "127.0.0.1", 81}, 1186 {"TCP", "127.0.0.1", 82}, 1187 {"TCP", "0.0.0.0", 79}, 1188 {"UDP", "0.0.0.0", 80}, 1189 {"TCP", "0.0.0.0", 81}, 1190 {"TCP", "0.0.0.0", 82}, 1191 }, 1192 length: 0, 1193 }, 1194 { 1195 desc: "empty ip and protocol remove should work", 1196 added: []hostPortInfoParam{ 1197 {"TCP", "127.0.0.1", 79}, 1198 {"UDP", "127.0.0.1", 80}, 1199 {"TCP", "127.0.0.1", 81}, 1200 {"TCP", "127.0.0.1", 82}, 1201 {"TCP", "0.0.0.0", 79}, 1202 {"UDP", "0.0.0.0", 80}, 1203 {"TCP", "0.0.0.0", 81}, 1204 {"TCP", "0.0.0.0", 82}, 1205 }, 1206 removed: []hostPortInfoParam{ 1207 {"", "127.0.0.1", 79}, 1208 {"", "127.0.0.1", 81}, 1209 {"", "127.0.0.1", 82}, 1210 {"UDP", "127.0.0.1", 80}, 1211 {"", "", 79}, 1212 {"", "", 81}, 1213 {"", "", 82}, 1214 {"UDP", "", 80}, 1215 }, 1216 length: 0, 1217 }, 1218 } 1219 1220 for _, test := range tests { 1221 t.Run(test.desc, func(t *testing.T) { 1222 hp := make(HostPortInfo) 1223 for _, param := range test.added { 1224 hp.Add(param.ip, param.protocol, param.port) 1225 } 1226 for _, param := range test.removed { 1227 hp.Remove(param.ip, param.protocol, param.port) 1228 } 1229 if hp.Len() != test.length { 1230 t.Errorf("%v failed: expect length %d; got %d", test.desc, test.length, hp.Len()) 1231 t.Error(hp) 1232 } 1233 }) 1234 } 1235 } 1236 1237 func TestHostPortInfo_Check(t *testing.T) { 1238 tests := []struct { 1239 desc string 1240 added []hostPortInfoParam 1241 check hostPortInfoParam 1242 expect bool 1243 }{ 1244 { 1245 desc: "empty check should check 0.0.0.0 and TCP", 1246 added: []hostPortInfoParam{ 1247 {"TCP", "127.0.0.1", 80}, 1248 }, 1249 check: hostPortInfoParam{"", "", 81}, 1250 expect: false, 1251 }, 1252 { 1253 desc: "empty check should check 0.0.0.0 and TCP (conflicted)", 1254 added: []hostPortInfoParam{ 1255 {"TCP", "127.0.0.1", 80}, 1256 }, 1257 check: hostPortInfoParam{"", "", 80}, 1258 expect: true, 1259 }, 1260 { 1261 desc: "empty port check should pass", 1262 added: []hostPortInfoParam{ 1263 {"TCP", "127.0.0.1", 80}, 1264 }, 1265 check: hostPortInfoParam{"", "", 0}, 1266 expect: false, 1267 }, 1268 { 1269 desc: "0.0.0.0 should check all registered IPs", 1270 added: []hostPortInfoParam{ 1271 {"TCP", "127.0.0.1", 80}, 1272 }, 1273 check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, 1274 expect: true, 1275 }, 1276 { 1277 desc: "0.0.0.0 with different protocol should be allowed", 1278 added: []hostPortInfoParam{ 1279 {"UDP", "127.0.0.1", 80}, 1280 }, 1281 check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, 1282 expect: false, 1283 }, 1284 { 1285 desc: "0.0.0.0 with different port should be allowed", 1286 added: []hostPortInfoParam{ 1287 {"TCP", "127.0.0.1", 79}, 1288 {"TCP", "127.0.0.1", 81}, 1289 {"TCP", "127.0.0.1", 82}, 1290 }, 1291 check: hostPortInfoParam{"TCP", "0.0.0.0", 80}, 1292 expect: false, 1293 }, 1294 { 1295 desc: "normal ip should check all registered 0.0.0.0", 1296 added: []hostPortInfoParam{ 1297 {"TCP", "0.0.0.0", 80}, 1298 }, 1299 check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, 1300 expect: true, 1301 }, 1302 { 1303 desc: "normal ip with different port/protocol should be allowed (0.0.0.0)", 1304 added: []hostPortInfoParam{ 1305 {"TCP", "0.0.0.0", 79}, 1306 {"UDP", "0.0.0.0", 80}, 1307 {"TCP", "0.0.0.0", 81}, 1308 {"TCP", "0.0.0.0", 82}, 1309 }, 1310 check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, 1311 expect: false, 1312 }, 1313 { 1314 desc: "normal ip with different port/protocol should be allowed", 1315 added: []hostPortInfoParam{ 1316 {"TCP", "127.0.0.1", 79}, 1317 {"UDP", "127.0.0.1", 80}, 1318 {"TCP", "127.0.0.1", 81}, 1319 {"TCP", "127.0.0.1", 82}, 1320 }, 1321 check: hostPortInfoParam{"TCP", "127.0.0.1", 80}, 1322 expect: false, 1323 }, 1324 } 1325 1326 for _, test := range tests { 1327 t.Run(test.desc, func(t *testing.T) { 1328 hp := make(HostPortInfo) 1329 for _, param := range test.added { 1330 hp.Add(param.ip, param.protocol, param.port) 1331 } 1332 if hp.CheckConflict(test.check.ip, test.check.protocol, test.check.port) != test.expect { 1333 t.Errorf("expected %t; got %t", test.expect, !test.expect) 1334 } 1335 }) 1336 } 1337 } 1338 1339 func TestGetNamespacesFromPodAffinityTerm(t *testing.T) { 1340 tests := []struct { 1341 name string 1342 term *v1.PodAffinityTerm 1343 want sets.Set[string] 1344 }{ 1345 { 1346 name: "podAffinityTerm_namespace_empty", 1347 term: &v1.PodAffinityTerm{}, 1348 want: sets.Set[string]{metav1.NamespaceDefault: sets.Empty{}}, 1349 }, 1350 { 1351 name: "podAffinityTerm_namespace_not_empty", 1352 term: &v1.PodAffinityTerm{ 1353 Namespaces: []string{metav1.NamespacePublic, metav1.NamespaceSystem}, 1354 }, 1355 want: sets.New(metav1.NamespacePublic, metav1.NamespaceSystem), 1356 }, 1357 { 1358 name: "podAffinityTerm_namespace_selector_not_nil", 1359 term: &v1.PodAffinityTerm{ 1360 NamespaceSelector: &metav1.LabelSelector{}, 1361 }, 1362 want: sets.Set[string]{}, 1363 }, 1364 } 1365 1366 for _, test := range tests { 1367 t.Run(test.name, func(t *testing.T) { 1368 got := getNamespacesFromPodAffinityTerm(&v1.Pod{ 1369 ObjectMeta: metav1.ObjectMeta{ 1370 Name: "topologies_pod", 1371 Namespace: metav1.NamespaceDefault, 1372 }, 1373 }, test.term) 1374 if diff := cmp.Diff(test.want, got); diff != "" { 1375 t.Errorf("unexpected diff (-want, +got):\n%s", diff) 1376 } 1377 }) 1378 } 1379 } 1380 1381 func TestFitError_Error(t *testing.T) { 1382 tests := []struct { 1383 name string 1384 pod *v1.Pod 1385 numAllNodes int 1386 diagnosis Diagnosis 1387 wantReasonMsg string 1388 }{ 1389 { 1390 name: "nodes failed Prefilter plugin", 1391 numAllNodes: 3, 1392 diagnosis: Diagnosis{ 1393 PreFilterMsg: "Node(s) failed PreFilter plugin FalsePreFilter", 1394 NodeToStatusMap: NodeToStatusMap{ 1395 // They're inserted by the framework. 1396 // We don't include them in the reason message because they'd be just duplicates. 1397 "node1": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1398 "node2": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1399 "node3": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1400 }, 1401 }, 1402 wantReasonMsg: "0/3 nodes are available: Node(s) failed PreFilter plugin FalsePreFilter.", 1403 }, 1404 { 1405 name: "nodes failed Prefilter plugin and the preemption also failed", 1406 numAllNodes: 3, 1407 diagnosis: Diagnosis{ 1408 PreFilterMsg: "Node(s) failed PreFilter plugin FalsePreFilter", 1409 NodeToStatusMap: NodeToStatusMap{ 1410 // They're inserted by the framework. 1411 // We don't include them in the reason message because they'd be just duplicates. 1412 "node1": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1413 "node2": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1414 "node3": NewStatus(Unschedulable, "Node(s) failed PreFilter plugin FalsePreFilter"), 1415 }, 1416 // PostFilterMsg will be included. 1417 PostFilterMsg: "Error running PostFilter plugin FailedPostFilter", 1418 }, 1419 wantReasonMsg: "0/3 nodes are available: Node(s) failed PreFilter plugin FalsePreFilter. Error running PostFilter plugin FailedPostFilter", 1420 }, 1421 { 1422 name: "nodes failed one Filter plugin with an empty PostFilterMsg", 1423 numAllNodes: 3, 1424 diagnosis: Diagnosis{ 1425 PreFilterMsg: "", 1426 NodeToStatusMap: NodeToStatusMap{ 1427 "node1": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1428 "node2": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1429 "node3": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1430 }, 1431 }, 1432 wantReasonMsg: "0/3 nodes are available: 3 Node(s) failed Filter plugin FalseFilter-1.", 1433 }, 1434 { 1435 name: "nodes failed one Filter plugin with a non-empty PostFilterMsg", 1436 numAllNodes: 3, 1437 diagnosis: Diagnosis{ 1438 PreFilterMsg: "", 1439 NodeToStatusMap: NodeToStatusMap{ 1440 "node1": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1441 "node2": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1442 "node3": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1443 }, 1444 PostFilterMsg: "Error running PostFilter plugin FailedPostFilter", 1445 }, 1446 wantReasonMsg: "0/3 nodes are available: 3 Node(s) failed Filter plugin FalseFilter-1. Error running PostFilter plugin FailedPostFilter", 1447 }, 1448 { 1449 name: "nodes failed two Filter plugins with an empty PostFilterMsg", 1450 numAllNodes: 3, 1451 diagnosis: Diagnosis{ 1452 PreFilterMsg: "", 1453 NodeToStatusMap: NodeToStatusMap{ 1454 "node1": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1455 "node2": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1456 "node3": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-2"), 1457 }, 1458 }, 1459 wantReasonMsg: "0/3 nodes are available: 1 Node(s) failed Filter plugin FalseFilter-2, 2 Node(s) failed Filter plugin FalseFilter-1.", 1460 }, 1461 { 1462 name: "nodes failed two Filter plugins with a non-empty PostFilterMsg", 1463 numAllNodes: 3, 1464 diagnosis: Diagnosis{ 1465 PreFilterMsg: "", 1466 NodeToStatusMap: NodeToStatusMap{ 1467 "node1": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1468 "node2": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-1"), 1469 "node3": NewStatus(Unschedulable, "Node(s) failed Filter plugin FalseFilter-2"), 1470 }, 1471 PostFilterMsg: "Error running PostFilter plugin FailedPostFilter", 1472 }, 1473 wantReasonMsg: "0/3 nodes are available: 1 Node(s) failed Filter plugin FalseFilter-2, 2 Node(s) failed Filter plugin FalseFilter-1. Error running PostFilter plugin FailedPostFilter", 1474 }, 1475 { 1476 name: "failed to Permit on node", 1477 numAllNodes: 1, 1478 diagnosis: Diagnosis{ 1479 NodeToStatusMap: NodeToStatusMap{ 1480 // There should be only one node here. 1481 "node1": NewStatus(Unschedulable, "Node failed Permit plugin Permit-1"), 1482 }, 1483 }, 1484 wantReasonMsg: "0/1 nodes are available: 1 Node failed Permit plugin Permit-1.", 1485 }, 1486 { 1487 name: "failed to Reserve on node", 1488 numAllNodes: 1, 1489 diagnosis: Diagnosis{ 1490 NodeToStatusMap: NodeToStatusMap{ 1491 // There should be only one node here. 1492 "node1": NewStatus(Unschedulable, "Node failed Reserve plugin Reserve-1"), 1493 }, 1494 }, 1495 wantReasonMsg: "0/1 nodes are available: 1 Node failed Reserve plugin Reserve-1.", 1496 }, 1497 } 1498 for _, tt := range tests { 1499 t.Run(tt.name, func(t *testing.T) { 1500 f := &FitError{ 1501 Pod: tt.pod, 1502 NumAllNodes: tt.numAllNodes, 1503 Diagnosis: tt.diagnosis, 1504 } 1505 if gotReasonMsg := f.Error(); gotReasonMsg != tt.wantReasonMsg { 1506 t.Errorf("Error() = Got: %v Want: %v", gotReasonMsg, tt.wantReasonMsg) 1507 } 1508 }) 1509 } 1510 } 1511 1512 func TestCalculatePodResourcesWithResize(t *testing.T) { 1513 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)() 1514 cpu500m := resource.MustParse("500m") 1515 mem500M := resource.MustParse("500Mi") 1516 cpu700m := resource.MustParse("700m") 1517 mem800M := resource.MustParse("800Mi") 1518 testpod := v1.Pod{ 1519 ObjectMeta: metav1.ObjectMeta{ 1520 Namespace: "pod_resize_test", 1521 Name: "testpod", 1522 UID: types.UID("testpod"), 1523 }, 1524 Spec: v1.PodSpec{ 1525 Containers: []v1.Container{ 1526 { 1527 Name: "c1", 1528 Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}}, 1529 }, 1530 }, 1531 }, 1532 Status: v1.PodStatus{ 1533 Phase: v1.PodRunning, 1534 Resize: "", 1535 ContainerStatuses: []v1.ContainerStatus{ 1536 { 1537 Name: "c1", 1538 AllocatedResources: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1539 }, 1540 }, 1541 }, 1542 } 1543 1544 tests := []struct { 1545 name string 1546 requests v1.ResourceList 1547 allocatedResources v1.ResourceList 1548 resizeStatus v1.PodResizeStatus 1549 expectedResource Resource 1550 expectedNon0CPU int64 1551 expectedNon0Mem int64 1552 }{ 1553 { 1554 name: "Pod with no pending resize", 1555 requests: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1556 allocatedResources: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1557 resizeStatus: "", 1558 expectedResource: Resource{MilliCPU: cpu500m.MilliValue(), Memory: mem500M.Value()}, 1559 expectedNon0CPU: cpu500m.MilliValue(), 1560 expectedNon0Mem: mem500M.Value(), 1561 }, 1562 { 1563 name: "Pod with resize in progress", 1564 requests: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1565 allocatedResources: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1566 resizeStatus: v1.PodResizeStatusInProgress, 1567 expectedResource: Resource{MilliCPU: cpu500m.MilliValue(), Memory: mem500M.Value()}, 1568 expectedNon0CPU: cpu500m.MilliValue(), 1569 expectedNon0Mem: mem500M.Value(), 1570 }, 1571 { 1572 name: "Pod with deferred resize", 1573 requests: v1.ResourceList{v1.ResourceCPU: cpu700m, v1.ResourceMemory: mem800M}, 1574 allocatedResources: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1575 resizeStatus: v1.PodResizeStatusDeferred, 1576 expectedResource: Resource{MilliCPU: cpu700m.MilliValue(), Memory: mem800M.Value()}, 1577 expectedNon0CPU: cpu700m.MilliValue(), 1578 expectedNon0Mem: mem800M.Value(), 1579 }, 1580 { 1581 name: "Pod with infeasible resize", 1582 requests: v1.ResourceList{v1.ResourceCPU: cpu700m, v1.ResourceMemory: mem800M}, 1583 allocatedResources: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M}, 1584 resizeStatus: v1.PodResizeStatusInfeasible, 1585 expectedResource: Resource{MilliCPU: cpu500m.MilliValue(), Memory: mem500M.Value()}, 1586 expectedNon0CPU: cpu500m.MilliValue(), 1587 expectedNon0Mem: mem500M.Value(), 1588 }, 1589 } 1590 1591 for _, tt := range tests { 1592 t.Run(tt.name, func(t *testing.T) { 1593 pod := testpod.DeepCopy() 1594 pod.Spec.Containers[0].Resources.Requests = tt.requests 1595 pod.Status.ContainerStatuses[0].AllocatedResources = tt.allocatedResources 1596 pod.Status.Resize = tt.resizeStatus 1597 1598 res, non0CPU, non0Mem := calculateResource(pod) 1599 if !reflect.DeepEqual(tt.expectedResource, res) { 1600 t.Errorf("Test: %s expected resource: %+v, got: %+v", tt.name, tt.expectedResource, res) 1601 } 1602 if non0CPU != tt.expectedNon0CPU { 1603 t.Errorf("Test: %s expected non0CPU: %d, got: %d", tt.name, tt.expectedNon0CPU, non0CPU) 1604 } 1605 if non0Mem != tt.expectedNon0Mem { 1606 t.Errorf("Test: %s expected non0Mem: %d, got: %d", tt.name, tt.expectedNon0Mem, non0Mem) 1607 } 1608 }) 1609 } 1610 }