@@ -18,6 +18,7 @@ package scheduler
18
18
19
19
import (
20
20
"context"
21
+ "fmt"
21
22
"reflect"
22
23
"testing"
23
24
"time"
@@ -30,8 +31,10 @@ import (
30
31
resourceapi "k8s.io/api/resource/v1beta1"
31
32
storagev1 "k8s.io/api/storage/v1"
32
33
"k8s.io/apimachinery/pkg/api/resource"
34
+ "k8s.io/apimachinery/pkg/util/sets"
33
35
utilfeature "k8s.io/apiserver/pkg/util/feature"
34
36
featuregatetesting "k8s.io/component-base/featuregate/testing"
37
+ "k8s.io/klog/v2"
35
38
"k8s.io/klog/v2/ktesting"
36
39
37
40
"k8s.io/apimachinery/pkg/runtime"
@@ -42,18 +45,168 @@ import (
42
45
"k8s.io/client-go/kubernetes/fake"
43
46
44
47
"k8s.io/kubernetes/pkg/features"
45
- "k8s.io/kubernetes/pkg/scheduler/backend/cache"
46
- "k8s.io/kubernetes/pkg/scheduler/backend/queue"
48
+ internalcache "k8s.io/kubernetes/pkg/scheduler/backend/cache"
49
+ internalqueue "k8s.io/kubernetes/pkg/scheduler/backend/queue"
47
50
"k8s.io/kubernetes/pkg/scheduler/framework"
48
51
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
49
52
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
50
53
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
51
54
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
55
+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins/queuesort"
52
56
"k8s.io/kubernetes/pkg/scheduler/metrics"
53
57
st "k8s.io/kubernetes/pkg/scheduler/testing"
58
+ "k8s.io/kubernetes/pkg/scheduler/util"
54
59
"k8s.io/kubernetes/pkg/scheduler/util/assumecache"
55
60
)
56
61
62
+ func TestEventHandlers_MoveToActiveOnNominatedNodeUpdate (t * testing.T ) {
63
+ metrics .Register ()
64
+ highPriorityPod :=
65
+ st .MakePod ().Name ("hpp" ).Namespace ("ns1" ).UID ("hppns1" ).Priority (highPriority ).SchedulerName (testSchedulerName ).Obj ()
66
+
67
+ medNominatedPriorityPod :=
68
+ st .MakePod ().Name ("mpp" ).Namespace ("ns2" ).UID ("mppns1" ).Priority (midPriority ).SchedulerName (testSchedulerName ).NominatedNodeName ("node1" ).Obj ()
69
+ medPriorityPod :=
70
+ st .MakePod ().Name ("smpp" ).Namespace ("ns3" ).UID ("mppns2" ).Priority (midPriority ).SchedulerName (testSchedulerName ).Obj ()
71
+
72
+ lowPriorityPod :=
73
+ st .MakePod ().Name ("lpp" ).Namespace ("ns4" ).UID ("lppns1" ).Priority (lowPriority ).SchedulerName (testSchedulerName ).Obj ()
74
+
75
+ unschedulablePods := []* v1.Pod {highPriorityPod , medNominatedPriorityPod , medPriorityPod , lowPriorityPod }
76
+
77
+ // Make pods schedulable on Delete event when QHints are enabled, but not when nominated node appears.
78
+ queueHintForPodDelete := func (logger klog.Logger , pod * v1.Pod , oldObj , newObj interface {}) (framework.QueueingHint , error ) {
79
+ oldPod , _ , err := util .As [* v1.Pod ](oldObj , newObj )
80
+ if err != nil {
81
+ t .Errorf ("Failed to convert objects to pods: %v" , err )
82
+ }
83
+ if oldPod .Status .NominatedNodeName == "" {
84
+ return framework .QueueSkip , nil
85
+ }
86
+ return framework .Queue , nil
87
+ }
88
+ queueingHintMap := internalqueue.QueueingHintMapPerProfile {
89
+ testSchedulerName : {
90
+ framework .EventAssignedPodDelete : {
91
+ {
92
+ PluginName : "fooPlugin1" ,
93
+ QueueingHintFn : queueHintForPodDelete ,
94
+ },
95
+ },
96
+ },
97
+ }
98
+
99
+ tests := []struct {
100
+ name string
101
+ updateFunc func (s * Scheduler )
102
+ wantInActive sets.Set [string ]
103
+ }{
104
+ {
105
+ name : "Update of a nominated node name to a different value should trigger rescheduling of lower priority pods" ,
106
+ updateFunc : func (s * Scheduler ) {
107
+ updatedPod := medNominatedPriorityPod .DeepCopy ()
108
+ updatedPod .Status .NominatedNodeName = "node2"
109
+ updatedPod .ResourceVersion = "1"
110
+ s .updatePodInSchedulingQueue (medNominatedPriorityPod , updatedPod )
111
+ },
112
+ wantInActive : sets .New (lowPriorityPod .Name , medPriorityPod .Name , medNominatedPriorityPod .Name ),
113
+ },
114
+ {
115
+ name : "Removal of a nominated node name should trigger rescheduling of lower priority pods" ,
116
+ updateFunc : func (s * Scheduler ) {
117
+ updatedPod := medNominatedPriorityPod .DeepCopy ()
118
+ updatedPod .Status .NominatedNodeName = ""
119
+ updatedPod .ResourceVersion = "1"
120
+ s .updatePodInSchedulingQueue (medNominatedPriorityPod , updatedPod )
121
+ },
122
+ wantInActive : sets .New (lowPriorityPod .Name , medPriorityPod .Name , medNominatedPriorityPod .Name ),
123
+ },
124
+ {
125
+ name : "Removal of a pod that had nominated node name should trigger rescheduling of lower priority pods" ,
126
+ updateFunc : func (s * Scheduler ) {
127
+ s .deletePodFromSchedulingQueue (medNominatedPriorityPod )
128
+ },
129
+ wantInActive : sets .New (lowPriorityPod .Name , medPriorityPod .Name ),
130
+ },
131
+ {
132
+ name : "Addition of a nominated node name to the high priority pod that did not have it before shouldn't trigger rescheduling" ,
133
+ updateFunc : func (s * Scheduler ) {
134
+ updatedPod := highPriorityPod .DeepCopy ()
135
+ updatedPod .Status .NominatedNodeName = "node2"
136
+ updatedPod .ResourceVersion = "1"
137
+ s .updatePodInSchedulingQueue (highPriorityPod , updatedPod )
138
+ },
139
+ wantInActive : sets .New [string ](),
140
+ },
141
+ }
142
+
143
+ for _ , tt := range tests {
144
+ for _ , qHintEnabled := range []bool {false , true } {
145
+ t .Run (fmt .Sprintf ("%s, with queuehint(%v)" , tt .name , qHintEnabled ), func (t * testing.T ) {
146
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .SchedulerQueueingHints , qHintEnabled )
147
+
148
+ logger , ctx := ktesting .NewTestContext (t )
149
+ ctx , cancel := context .WithCancel (ctx )
150
+ defer cancel ()
151
+
152
+ var objs []runtime.Object
153
+ for _ , pod := range unschedulablePods {
154
+ objs = append (objs , pod )
155
+ }
156
+ client := fake .NewClientset (objs ... )
157
+ informerFactory := informers .NewSharedInformerFactory (client , 0 )
158
+
159
+ recorder := metrics .NewMetricsAsyncRecorder (3 , 20 * time .Microsecond , ctx .Done ())
160
+ queue := internalqueue .NewPriorityQueue (
161
+ newDefaultQueueSort (),
162
+ informerFactory ,
163
+ internalqueue .WithMetricsRecorder (* recorder ),
164
+ internalqueue .WithQueueingHintMapPerProfile (queueingHintMap ),
165
+ // disable backoff queue
166
+ internalqueue .WithPodInitialBackoffDuration (0 ),
167
+ internalqueue .WithPodMaxBackoffDuration (0 ))
168
+ schedulerCache := internalcache .New (ctx , 30 * time .Second )
169
+
170
+ // Put test pods into unschedulable queue
171
+ for _ , pod := range unschedulablePods {
172
+ queue .Add (logger , pod )
173
+ poppedPod , err := queue .Pop (logger )
174
+ if err != nil {
175
+ t .Fatalf ("Pop failed: %v" , err )
176
+ }
177
+ poppedPod .UnschedulablePlugins = sets .New ("fooPlugin1" )
178
+ if err := queue .AddUnschedulableIfNotPresent (logger , poppedPod , queue .SchedulingCycle ()); err != nil {
179
+ t .Errorf ("Unexpected error from AddUnschedulableIfNotPresent: %v" , err )
180
+ }
181
+ }
182
+
183
+ s , _ , err := initScheduler (ctx , schedulerCache , queue , client , informerFactory )
184
+ if err != nil {
185
+ t .Fatalf ("Failed to initialize test scheduler: %v" , err )
186
+ }
187
+
188
+ if len (s .SchedulingQueue .PodsInActiveQ ()) > 0 {
189
+ t .Errorf ("No pods were expected to be in the activeQ before the update, but there were %v" , s .SchedulingQueue .PodsInActiveQ ())
190
+ }
191
+ tt .updateFunc (s )
192
+ if len (s .SchedulingQueue .PodsInActiveQ ()) != len (tt .wantInActive ) {
193
+ t .Errorf ("Different number of pods were expected to be in the activeQ, but found actual %v vs. expected %v" , s .SchedulingQueue .PodsInActiveQ (), tt .wantInActive )
194
+ }
195
+ for _ , pod := range s .SchedulingQueue .PodsInActiveQ () {
196
+ if ! tt .wantInActive .Has (pod .Name ) {
197
+ t .Errorf ("Found unexpected pod in activeQ: %s" , pod .Name )
198
+ }
199
+ }
200
+ })
201
+ }
202
+ }
203
+ }
204
+
205
+ func newDefaultQueueSort () framework.LessFunc {
206
+ sort := & queuesort.PrioritySort {}
207
+ return sort .Less
208
+ }
209
+
57
210
func TestUpdatePodInCache (t * testing.T ) {
58
211
ttl := 10 * time .Second
59
212
nodeName := "node"
@@ -81,8 +234,8 @@ func TestUpdatePodInCache(t *testing.T) {
81
234
ctx , cancel := context .WithCancel (ctx )
82
235
defer cancel ()
83
236
sched := & Scheduler {
84
- Cache : cache .New (ctx , ttl ),
85
- SchedulingQueue : queue .NewTestQueue (ctx , nil ),
237
+ Cache : internalcache .New (ctx , ttl ),
238
+ SchedulingQueue : internalqueue .NewTestQueue (ctx , nil ),
86
239
logger : logger ,
87
240
}
88
241
sched .addPodToCache (tt .oldObj )
@@ -354,7 +507,7 @@ func TestAddAllEventHandlers(t *testing.T) {
354
507
defer cancel ()
355
508
356
509
informerFactory := informers .NewSharedInformerFactory (fake .NewClientset (), 0 )
357
- schedulingQueue := queue .NewTestQueueWithInformerFactory (ctx , nil , informerFactory )
510
+ schedulingQueue := internalqueue .NewTestQueueWithInformerFactory (ctx , nil , informerFactory )
358
511
testSched := Scheduler {
359
512
StopEverything : ctx .Done (),
360
513
SchedulingQueue : schedulingQueue ,
0 commit comments