@@ -22,6 +22,7 @@ import (
22
22
"fmt"
23
23
"reflect"
24
24
"sort"
25
+ "strings"
25
26
"sync"
26
27
"testing"
27
28
"time"
@@ -441,12 +442,16 @@ func TestPrepareCandidate(t *testing.T) {
441
442
)
442
443
443
444
tests := []struct {
444
- name string
445
- nodeNames []string
446
- candidate * fakeCandidate
447
- preemptor * v1.Pod
448
- testPods []* v1.Pod
449
- expectedDeletedPods []string
445
+ name string
446
+ nodeNames []string
447
+ candidate * fakeCandidate
448
+ preemptor * v1.Pod
449
+ testPods []* v1.Pod
450
+ // expectedDeletedPod is the pod name that is expected to be deleted.
451
+ //
452
+ // You can set multiple pod name if there're multiple possibilities.
453
+ // Both empty and "" means no pod is expected to be deleted.
454
+ expectedDeletedPod []string
450
455
expectedDeletionError bool
451
456
expectedPatchError bool
452
457
// Only compared when async preemption is disabled.
@@ -457,7 +462,6 @@ func TestPrepareCandidate(t *testing.T) {
457
462
}{
458
463
{
459
464
name : "no victims" ,
460
-
461
465
candidate : & fakeCandidate {
462
466
victims : & extenderv1.Victims {},
463
467
},
@@ -485,7 +489,7 @@ func TestPrepareCandidate(t *testing.T) {
485
489
victim1 ,
486
490
},
487
491
nodeNames : []string {node1Name },
488
- expectedDeletedPods : []string {"victim1" },
492
+ expectedDeletedPod : []string {"victim1" },
489
493
expectedStatus : nil ,
490
494
expectedPreemptingMap : sets .New (types .UID ("preemptor" )),
491
495
},
@@ -505,7 +509,7 @@ func TestPrepareCandidate(t *testing.T) {
505
509
victim1WithMatchingCondition ,
506
510
},
507
511
nodeNames : []string {node1Name },
508
- expectedDeletedPods : []string {"victim1" },
512
+ expectedDeletedPod : []string {"victim1" },
509
513
expectedStatus : nil ,
510
514
expectedPreemptingMap : sets .New (types .UID ("preemptor" )),
511
515
},
@@ -523,7 +527,7 @@ func TestPrepareCandidate(t *testing.T) {
523
527
preemptor : preemptor ,
524
528
testPods : []* v1.Pod {},
525
529
nodeNames : []string {node1Name },
526
- expectedDeletedPods : []string {"victim1" },
530
+ expectedDeletedPod : []string {"victim1" },
527
531
expectedStatus : nil ,
528
532
expectedPreemptingMap : sets .New (types .UID ("preemptor" )),
529
533
},
@@ -560,7 +564,7 @@ func TestPrepareCandidate(t *testing.T) {
560
564
preemptor : preemptor ,
561
565
testPods : []* v1.Pod {},
562
566
nodeNames : []string {node1Name },
563
- expectedDeletedPods : []string {"victim1" },
567
+ expectedDeletedPod : []string {"victim1" },
564
568
expectedStatus : nil ,
565
569
expectedPreemptingMap : sets .New (types .UID ("preemptor" )),
566
570
},
@@ -599,9 +603,14 @@ func TestPrepareCandidate(t *testing.T) {
599
603
testPods : []* v1.Pod {
600
604
victim1 ,
601
605
},
602
- nodeNames : []string {node1Name },
603
- expectedPatchError : true ,
604
- expectedDeletedPods : []string {"victim2" },
606
+ nodeNames : []string {node1Name },
607
+ expectedPatchError : true ,
608
+ expectedDeletedPod : []string {
609
+ "victim2" ,
610
+ // The first victim could fail before the deletion of the second victim happens,
611
+ // which results in the second victim not being deleted.
612
+ "" ,
613
+ },
605
614
expectedStatus : framework .AsStatus (errors .New ("patch pod status failed" )),
606
615
expectedPreemptingMap : sets .New (types .UID ("preemptor" )),
607
616
expectedActivatedPods : map [string ]* v1.Pod {preemptor .Name : preemptor },
@@ -629,15 +638,13 @@ func TestPrepareCandidate(t *testing.T) {
629
638
objs = append (objs , pod )
630
639
}
631
640
632
- requestStopper := make (chan struct {})
633
641
mu := & sync.RWMutex {}
634
642
deletedPods := sets .New [string ]()
635
643
deletionFailure := false // whether any request to delete pod failed
636
644
patchFailure := false // whether any request to patch pod status failed
637
645
638
646
cs := clientsetfake .NewClientset (objs ... )
639
647
cs .PrependReactor ("delete" , "pods" , func (action clienttesting.Action ) (bool , runtime.Object , error ) {
640
- <- requestStopper
641
648
mu .Lock ()
642
649
defer mu .Unlock ()
643
650
name := action .(clienttesting.DeleteAction ).GetName ()
@@ -651,7 +658,6 @@ func TestPrepareCandidate(t *testing.T) {
651
658
})
652
659
653
660
cs .PrependReactor ("patch" , "pods" , func (action clienttesting.Action ) (bool , runtime.Object , error ) {
654
- <- requestStopper
655
661
mu .Lock ()
656
662
defer mu .Unlock ()
657
663
if action .(clienttesting.PatchAction ).GetName () == "fail-victim" {
@@ -664,6 +670,15 @@ func TestPrepareCandidate(t *testing.T) {
664
670
informerFactory := informers .NewSharedInformerFactory (cs , 0 )
665
671
eventBroadcaster := events .NewBroadcaster (& events.EventSinkImpl {Interface : cs .EventsV1 ()})
666
672
fakeActivator := & fakePodActivator {activatedPods : make (map [string ]* v1.Pod ), mu : mu }
673
+
674
+ // Note: NominatedPodsForNode is called at the beginning of the goroutine in any case.
675
+ // fakePodNominator can delay the response of NominatedPodsForNode until the channel is closed,
676
+ // which allows us to test the preempting map before the goroutine does nothing yet.
677
+ requestStopper := make (chan struct {})
678
+ nominator := & fakePodNominator {
679
+ SchedulingQueue : internalqueue .NewSchedulingQueue (nil , informerFactory ),
680
+ requestStopper : requestStopper ,
681
+ }
667
682
fwk , err := tf .NewFramework (
668
683
ctx ,
669
684
registeredPlugins , "" ,
@@ -672,7 +687,7 @@ func TestPrepareCandidate(t *testing.T) {
672
687
frameworkruntime .WithInformerFactory (informerFactory ),
673
688
frameworkruntime .WithWaitingPods (frameworkruntime .NewWaitingPodsMap ()),
674
689
frameworkruntime .WithSnapshotSharedLister (internalcache .NewSnapshot (tt .testPods , nodes )),
675
- frameworkruntime .WithPodNominator (internalqueue . NewSchedulingQueue ( nil , informerFactory ) ),
690
+ frameworkruntime .WithPodNominator (nominator ),
676
691
frameworkruntime .WithEventRecorder (eventBroadcaster .NewRecorder (scheme .Scheme , "test-scheduler" )),
677
692
frameworkruntime .WithPodActivator (fakeActivator ),
678
693
)
@@ -720,10 +735,15 @@ func TestPrepareCandidate(t *testing.T) {
720
735
if err := wait .PollUntilContextTimeout (ctx , time .Millisecond * 200 , wait .ForeverTestTimeout , false , func (ctx context.Context ) (bool , error ) {
721
736
mu .RLock ()
722
737
defer mu .RUnlock ()
723
- if ! deletedPods .Equal (sets .New (tt .expectedDeletedPods ... )) {
724
- lastErrMsg = fmt .Sprintf ("expected deleted pods %v, got %v" , tt .expectedDeletedPods , deletedPods .UnsortedList ())
738
+
739
+ pe .mu .Lock ()
740
+ defer pe .mu .Unlock ()
741
+ if len (pe .preempting ) != 0 {
742
+ // The preempting map should be empty after the goroutine in all test cases.
743
+ lastErrMsg = fmt .Sprintf ("expected no preempting pods, got %v" , pe .preempting )
725
744
return false , nil
726
745
}
746
+
727
747
if tt .expectedDeletionError != deletionFailure {
728
748
lastErrMsg = fmt .Sprintf ("expected deletion error %v, got %v" , tt .expectedDeletionError , deletionFailure )
729
749
return false , nil
@@ -744,6 +764,34 @@ func TestPrepareCandidate(t *testing.T) {
744
764
}
745
765
}
746
766
767
+ if deletedPods .Len () > 1 {
768
+ // For now, we only expect at most one pod to be deleted in all test cases.
769
+ // If we need to test multiple pods deletion, we need to update the test table definition.
770
+ return false , fmt .Errorf ("expected at most one pod to be deleted, got %v" , deletedPods .UnsortedList ())
771
+ }
772
+
773
+ if len (tt .expectedDeletedPod ) == 0 {
774
+ if deletedPods .Len () != 0 {
775
+ // When tt.expectedDeletedPod is empty, we expect no pod to be deleted.
776
+ return false , fmt .Errorf ("expected no pod to be deleted, got %v" , deletedPods .UnsortedList ())
777
+ }
778
+ // nothing further to check.
779
+ return true , nil
780
+ }
781
+
782
+ found := false
783
+ for _ , podName := range tt .expectedDeletedPod {
784
+ if deletedPods .Has (podName ) ||
785
+ // If podName is empty, we expect no pod to be deleted.
786
+ (deletedPods .Len () == 0 && podName == "" ) {
787
+ found = true
788
+ }
789
+ }
790
+ if ! found {
791
+ lastErrMsg = fmt .Sprintf ("expected pod %v to be deleted, but %v is deleted" , strings .Join (tt .expectedDeletedPod , " or " ), deletedPods .UnsortedList ())
792
+ return false , nil
793
+ }
794
+
747
795
return true , nil
748
796
}); err != nil {
749
797
t .Fatal (lastErrMsg )
@@ -753,6 +801,19 @@ func TestPrepareCandidate(t *testing.T) {
753
801
}
754
802
}
755
803
804
+ type fakePodNominator struct {
805
+ // embed it so that we can only override NominatedPodsForNode
806
+ internalqueue.SchedulingQueue
807
+
808
+ // fakePodNominator doesn't respond to NominatedPodsForNode() until the channel is closed.
809
+ requestStopper chan struct {}
810
+ }
811
+
812
+ func (f * fakePodNominator ) NominatedPodsForNode (nodeName string ) []* framework.PodInfo {
813
+ <- f .requestStopper
814
+ return nil
815
+ }
816
+
756
817
type fakeExtender struct {
757
818
ignorable bool
758
819
errProcessPreemption bool
0 commit comments