@@ -21,13 +21,16 @@ import (
21
21
"fmt"
22
22
"sort"
23
23
"strings"
24
+ "sync"
24
25
"testing"
25
26
"time"
26
27
27
28
"github.com/google/go-cmp/cmp"
28
29
v1 "k8s.io/api/core/v1"
30
+ eventsv1 "k8s.io/api/events/v1"
29
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
32
"k8s.io/apimachinery/pkg/runtime"
33
+ "k8s.io/apimachinery/pkg/util/sets"
31
34
utilfeature "k8s.io/apiserver/pkg/util/feature"
32
35
"k8s.io/client-go/informers"
33
36
"k8s.io/client-go/kubernetes"
@@ -437,12 +440,14 @@ func initScheduler(ctx context.Context, cache internalcache.Cache, queue interna
437
440
tf .RegisterBindPlugin (defaultbinder .Name , defaultbinder .New ),
438
441
}
439
442
eventBroadcaster := events .NewBroadcaster (& events.EventSinkImpl {Interface : client .EventsV1 ()})
443
+ waitingPods := frameworkruntime .NewWaitingPodsMap ()
440
444
fwk , err := tf .NewFramework (ctx ,
441
445
registerPluginFuncs ,
442
446
testSchedulerName ,
443
447
frameworkruntime .WithClientSet (client ),
444
448
frameworkruntime .WithInformerFactory (informerFactory ),
445
449
frameworkruntime .WithEventRecorder (eventBroadcaster .NewRecorder (scheme .Scheme , testSchedulerName )),
450
+ frameworkruntime .WithWaitingPods (waitingPods ),
446
451
)
447
452
if err != nil {
448
453
return nil , nil , err
@@ -591,6 +596,7 @@ const (
591
596
queueSort = "no-op-queue-sort-plugin"
592
597
fakeBind = "bind-plugin"
593
598
emptyEventExtensions = "emptyEventExtensions"
599
+ fakePermit = "fakePermit"
594
600
)
595
601
596
602
func Test_buildQueueingHintMap (t * testing.T ) {
@@ -905,6 +911,160 @@ func newFramework(ctx context.Context, r frameworkruntime.Registry, profile sche
905
911
)
906
912
}
907
913
914
+ func TestFrameworkHandler_IterateOverWaitingPods (t * testing.T ) {
915
+ const (
916
+ testSchedulerProfile1 = "test-scheduler-profile-1"
917
+ testSchedulerProfile2 = "test-scheduler-profile-2"
918
+ testSchedulerProfile3 = "test-scheduler-profile-3"
919
+ )
920
+
921
+ nodes := []runtime.Object {
922
+ st .MakeNode ().Name ("node1" ).UID ("node1" ).Obj (),
923
+ st .MakeNode ().Name ("node2" ).UID ("node2" ).Obj (),
924
+ st .MakeNode ().Name ("node3" ).UID ("node3" ).Obj (),
925
+ }
926
+
927
+ cases := []struct {
928
+ name string
929
+ profiles []schedulerapi.KubeSchedulerProfile
930
+ waitSchedulingPods []* v1.Pod
931
+ expectPodNamesInWaitingPods []string
932
+ }{
933
+ {
934
+ name : "pods with same profile are waiting on permit stage" ,
935
+ profiles : []schedulerapi.KubeSchedulerProfile {
936
+ {
937
+ SchedulerName : testSchedulerProfile1 ,
938
+ Plugins : & schedulerapi.Plugins {
939
+ QueueSort : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "PrioritySort" }}},
940
+ Permit : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : fakePermit }}},
941
+ Bind : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "DefaultBinder" }}},
942
+ },
943
+ },
944
+ },
945
+ waitSchedulingPods : []* v1.Pod {
946
+ st .MakePod ().Name ("pod1" ).UID ("pod1" ).SchedulerName (testSchedulerProfile1 ).Obj (),
947
+ st .MakePod ().Name ("pod2" ).UID ("pod2" ).SchedulerName (testSchedulerProfile1 ).Obj (),
948
+ st .MakePod ().Name ("pod3" ).UID ("pod3" ).SchedulerName (testSchedulerProfile1 ).Obj (),
949
+ },
950
+ expectPodNamesInWaitingPods : []string {"pod1" , "pod2" , "pod3" },
951
+ },
952
+ {
953
+ name : "pods with different profiles are waiting on permit stage" ,
954
+ profiles : []schedulerapi.KubeSchedulerProfile {
955
+ {
956
+ SchedulerName : testSchedulerProfile1 ,
957
+ Plugins : & schedulerapi.Plugins {
958
+ QueueSort : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "PrioritySort" }}},
959
+ Permit : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : fakePermit }}},
960
+ Bind : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "DefaultBinder" }}},
961
+ },
962
+ },
963
+ {
964
+ SchedulerName : testSchedulerProfile2 ,
965
+ Plugins : & schedulerapi.Plugins {
966
+ QueueSort : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "PrioritySort" }}},
967
+ Permit : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : fakePermit }}},
968
+ Bind : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "DefaultBinder" }}},
969
+ },
970
+ },
971
+ {
972
+ SchedulerName : testSchedulerProfile3 ,
973
+ Plugins : & schedulerapi.Plugins {
974
+ QueueSort : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "PrioritySort" }}},
975
+ Permit : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : fakePermit }}},
976
+ Bind : schedulerapi.PluginSet {Enabled : []schedulerapi.Plugin {{Name : "DefaultBinder" }}},
977
+ },
978
+ },
979
+ },
980
+ waitSchedulingPods : []* v1.Pod {
981
+ st .MakePod ().Name ("pod1" ).UID ("pod1" ).SchedulerName (testSchedulerProfile1 ).Obj (),
982
+ st .MakePod ().Name ("pod2" ).UID ("pod2" ).SchedulerName (testSchedulerProfile1 ).Obj (),
983
+ st .MakePod ().Name ("pod3" ).UID ("pod3" ).SchedulerName (testSchedulerProfile2 ).Obj (),
984
+ st .MakePod ().Name ("pod4" ).UID ("pod4" ).SchedulerName (testSchedulerProfile3 ).Obj (),
985
+ },
986
+ expectPodNamesInWaitingPods : []string {"pod1" , "pod2" , "pod3" , "pod4" },
987
+ },
988
+ }
989
+
990
+ for _ , tc := range cases {
991
+ t .Run (tc .name , func (t * testing.T ) {
992
+ // Set up scheduler for the 3 nodes.
993
+ objs := append ([]runtime.Object {& v1.Namespace {ObjectMeta : metav1.ObjectMeta {Name : "" }}}, nodes ... )
994
+ fakeClient := fake .NewSimpleClientset (objs ... )
995
+ informerFactory := informers .NewSharedInformerFactory (fakeClient , 0 )
996
+ eventBroadcaster := events .NewBroadcaster (& events.EventSinkImpl {Interface : fakeClient .EventsV1 ()})
997
+ eventRecorder := eventBroadcaster .NewRecorder (scheme .Scheme , fakePermit )
998
+
999
+ outOfTreeRegistry := frameworkruntime.Registry {
1000
+ fakePermit : newFakePermitPlugin (eventRecorder ),
1001
+ }
1002
+
1003
+ _ , ctx := ktesting .NewTestContext (t )
1004
+ ctx , cancel := context .WithCancel (ctx )
1005
+ defer cancel ()
1006
+
1007
+ scheduler , err := New (
1008
+ ctx ,
1009
+ fakeClient ,
1010
+ informerFactory ,
1011
+ nil ,
1012
+ profile .NewRecorderFactory (eventBroadcaster ),
1013
+ WithProfiles (tc .profiles ... ),
1014
+ WithFrameworkOutOfTreeRegistry (outOfTreeRegistry ),
1015
+ )
1016
+
1017
+ if err != nil {
1018
+ t .Fatalf ("Failed to create scheduler: %v" , err )
1019
+ }
1020
+
1021
+ var wg sync.WaitGroup
1022
+ waitSchedulingPodNumber := len (tc .waitSchedulingPods )
1023
+ wg .Add (waitSchedulingPodNumber )
1024
+ stopFn , err := eventBroadcaster .StartEventWatcher (func (obj runtime.Object ) {
1025
+ e , ok := obj .(* eventsv1.Event )
1026
+ if ! ok || (e .Reason != podWaitingReason ) {
1027
+ return
1028
+ }
1029
+ wg .Done ()
1030
+ })
1031
+ if err != nil {
1032
+ t .Fatal (err )
1033
+ }
1034
+ defer stopFn ()
1035
+
1036
+ // Run scheduler.
1037
+ informerFactory .Start (ctx .Done ())
1038
+ informerFactory .WaitForCacheSync (ctx .Done ())
1039
+ go scheduler .Run (ctx )
1040
+
1041
+ // Send pods to be scheduled.
1042
+ for _ , p := range tc .waitSchedulingPods {
1043
+ _ , err = fakeClient .CoreV1 ().Pods ("" ).Create (ctx , p , metav1.CreateOptions {})
1044
+ if err != nil {
1045
+ t .Fatal (err )
1046
+ }
1047
+ }
1048
+
1049
+ // Wait all pods in waitSchedulingPods to be scheduled.
1050
+ wg .Wait ()
1051
+
1052
+ // Ensure that all waitingPods in scheduler can be obtained from any profiles.
1053
+ for _ , fwk := range scheduler .Profiles {
1054
+ actualPodNamesInWaitingPods := sets .NewString ()
1055
+ fwk .IterateOverWaitingPods (func (pod framework.WaitingPod ) {
1056
+ actualPodNamesInWaitingPods .Insert (pod .GetPod ().Name )
1057
+ })
1058
+ // Validate the name of pods in waitingPods matches expectations.
1059
+ if actualPodNamesInWaitingPods .Len () != len (tc .expectPodNamesInWaitingPods ) ||
1060
+ ! actualPodNamesInWaitingPods .HasAll (tc .expectPodNamesInWaitingPods ... ) {
1061
+ t .Fatalf ("Unexpected waitingPods in scheduler profile %s, expect: %#v, got: %#v" , fwk .ProfileName (), tc .expectPodNamesInWaitingPods , actualPodNamesInWaitingPods .List ())
1062
+ }
1063
+ }
1064
+ })
1065
+ }
1066
+ }
1067
+
908
1068
var _ framework.QueueSortPlugin = & fakeQueueSortPlugin {}
909
1069
910
1070
// fakeQueueSortPlugin is a no-op implementation for QueueSort extension point.
@@ -1004,3 +1164,36 @@ func (*emptyEventsToRegisterPlugin) Filter(_ context.Context, _ *framework.Cycle
1004
1164
}
1005
1165
1006
1166
func (* emptyEventsToRegisterPlugin ) EventsToRegister () []framework.ClusterEventWithHint { return nil }
1167
+
1168
+ // fakePermitPlugin only implements PermitPlugin interface.
1169
+ type fakePermitPlugin struct {
1170
+ eventRecorder events.EventRecorder
1171
+ }
1172
+
1173
+ func newFakePermitPlugin (eventRecorder events.EventRecorder ) frameworkruntime.PluginFactory {
1174
+ return func (ctx context.Context , configuration runtime.Object , f framework.Handle ) (framework.Plugin , error ) {
1175
+ pl := & fakePermitPlugin {
1176
+ eventRecorder : eventRecorder ,
1177
+ }
1178
+ return pl , nil
1179
+ }
1180
+ }
1181
+
1182
+ func (f fakePermitPlugin ) Name () string {
1183
+ return fakePermit
1184
+ }
1185
+
1186
+ const (
1187
+ podWaitingReason = "podWaiting"
1188
+ )
1189
+
1190
+ func (f fakePermitPlugin ) Permit (ctx context.Context , state * framework.CycleState , p * v1.Pod , nodeName string ) (* framework.Status , time.Duration ) {
1191
+ defer func () {
1192
+ // Send event with podWaiting reason to broadcast this pod is already waiting in the permit stage.
1193
+ f .eventRecorder .Eventf (p , nil , v1 .EventTypeWarning , podWaitingReason , "" , "" )
1194
+ }()
1195
+
1196
+ return framework .NewStatus (framework .Wait ), 100 * time .Second
1197
+ }
1198
+
1199
+ var _ framework.PermitPlugin = & fakePermitPlugin {}
0 commit comments