1
16
17 package noderesources
18
19 import (
20 "context"
21 "testing"
22
23 "github.com/google/go-cmp/cmp"
24 v1 "k8s.io/api/core/v1"
25 "k8s.io/apimachinery/pkg/util/validation/field"
26 "k8s.io/klog/v2/ktesting"
27 "k8s.io/kubernetes/pkg/scheduler/apis/config"
28 "k8s.io/kubernetes/pkg/scheduler/framework"
29 plfeature "k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
30 "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
31 "k8s.io/kubernetes/pkg/scheduler/internal/cache"
32 st "k8s.io/kubernetes/pkg/scheduler/testing"
33 tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
34 )
35
36 func TestMostAllocatedScoringStrategy(t *testing.T) {
37 tests := []struct {
38 name string
39 requestedPod *v1.Pod
40 nodes []*v1.Node
41 existingPods []*v1.Pod
42 expectedScores framework.NodeScoreList
43 resources []config.ResourceSpec
44 wantErrs field.ErrorList
45 wantStatusCode framework.Code
46 }{
47 {
48
49
50
51
52
53
54
55
56 name: "nothing scheduled, nothing requested",
57 requestedPod: st.MakePod().Obj(),
58 nodes: []*v1.Node{
59 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
60 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
61 },
62 existingPods: nil,
63 expectedScores: []framework.NodeScore{{Name: "node1", Score: framework.MinNodeScore}, {Name: "node2", Score: framework.MinNodeScore}},
64 resources: defaultResources,
65 },
66 {
67
68
69
70
71
72
73
74
75 name: "nothing scheduled, resources requested, differently sized nodes",
76 requestedPod: st.MakePod().
77 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
78 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
79 Obj(),
80 nodes: []*v1.Node{
81 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
82 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
83 },
84 existingPods: nil,
85 expectedScores: []framework.NodeScore{{Name: "node1", Score: 62}, {Name: "node2", Score: 50}},
86 resources: defaultResources,
87 },
88 {
89 name: "Resources not set, pods scheduled with error",
90 requestedPod: st.MakePod().
91 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
92 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
93 Obj(),
94 nodes: []*v1.Node{
95 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
96 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
97 },
98 existingPods: nil,
99 expectedScores: []framework.NodeScore{{Name: "node1", Score: framework.MinNodeScore}, {Name: "node2", Score: framework.MinNodeScore}},
100 resources: nil,
101 wantStatusCode: framework.Error,
102 },
103 {
104
105
106
107
108
109
110
111
112 name: "no resources requested, pods scheduled with resources",
113 requestedPod: st.MakePod().Obj(),
114 nodes: []*v1.Node{
115 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "10000", "memory": "20000"}).Obj(),
116 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "10000", "memory": "20000"}).Obj(),
117 },
118 existingPods: []*v1.Pod{
119 st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "0"}).Obj(),
120 st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "0"}).Obj(),
121 st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "0"}).Obj(),
122 st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).Obj(),
123 },
124 expectedScores: []framework.NodeScore{{Name: "node1", Score: 30}, {Name: "node2", Score: 42}},
125 resources: defaultResources,
126 },
127 {
128
129
130
131
132
133
134
135
136 name: "resources requested, pods scheduled with resources",
137 requestedPod: st.MakePod().
138 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
139 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
140 Obj(),
141 nodes: []*v1.Node{
142 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "10000", "memory": "20000"}).Obj(),
143 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "10000", "memory": "20000"}).Obj(),
144 },
145 existingPods: []*v1.Pod{
146 st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "0"}).Obj(),
147 st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).Obj(),
148 },
149 expectedScores: []framework.NodeScore{{Name: "node1", Score: 42}, {Name: "node2", Score: 55}},
150 resources: defaultResources,
151 },
152 {
153
154
155
156
157
158
159
160
161 name: "resources requested equal node capacity",
162 requestedPod: st.MakePod().
163 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "4000"}).
164 Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).
165 Obj(),
166 nodes: []*v1.Node{
167 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "5000", "memory": "10000"}).Obj(),
168 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "10000", "memory": "9000"}).Obj(),
169 },
170 existingPods: nil,
171 expectedScores: []framework.NodeScore{{Name: "node1", Score: 95}, {Name: "node2", Score: 75}},
172 resources: defaultResources,
173 },
174 {
175
176
177
178
179
180
181 name: "nothing scheduled, resources requested, differently sized nodes",
182 requestedPod: st.MakePod().
183 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
184 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
185 Obj(),
186 nodes: []*v1.Node{
187 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
188 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
189 },
190 existingPods: nil,
191 expectedScores: []framework.NodeScore{{Name: "node1", Score: 58}, {Name: "node2", Score: 50}},
192 resources: []config.ResourceSpec{
193 {Name: "memory", Weight: 2},
194 {Name: "cpu", Weight: 1},
195 },
196 },
197 {
198
199
200
201
202
203
204
205
206 name: "no resources requested, pods scheduled, nonzero request for resource",
207 requestedPod: st.MakePod().Container("container").Obj(),
208 nodes: []*v1.Node{
209 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "250m", "memory": "1000Mi"}).Obj(),
210 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "250m", "memory": "1000Mi"}).Obj(),
211 },
212 existingPods: []*v1.Pod{
213 st.MakePod().Node("node1").Container("container").Obj(),
214 st.MakePod().Node("node1").Container("container").Obj(),
215 },
216 expectedScores: []framework.NodeScore{{Name: "node1", Score: 80}, {Name: "node2", Score: 30}},
217 resources: defaultResources,
218 },
219 {
220
221 name: "resource with negative weight",
222 requestedPod: st.MakePod().
223 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
224 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
225 Obj(),
226 nodes: []*v1.Node{
227 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
228 },
229 resources: []config.ResourceSpec{
230 {Name: "memory", Weight: -1},
231 {Name: "cpu", Weight: 1},
232 },
233 wantErrs: field.ErrorList{
234 &field.Error{
235 Type: field.ErrorTypeInvalid,
236 Field: "scoringStrategy.resources[0].weight",
237 },
238 },
239 },
240 {
241
242 name: "resource with zero weight",
243 requestedPod: st.MakePod().
244 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
245 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
246 Obj(),
247 nodes: []*v1.Node{
248 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
249 },
250 resources: []config.ResourceSpec{
251 {Name: "memory", Weight: 1},
252 {Name: "cpu", Weight: 0},
253 },
254 wantErrs: field.ErrorList{
255 &field.Error{
256 Type: field.ErrorTypeInvalid,
257 Field: "scoringStrategy.resources[1].weight",
258 },
259 },
260 },
261 {
262
263 name: "resource weight larger than MaxNodeScore",
264 requestedPod: st.MakePod().
265 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
266 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
267 Obj(),
268 nodes: []*v1.Node{
269 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
270 },
271 resources: []config.ResourceSpec{
272 {Name: "memory", Weight: 101},
273 },
274 wantErrs: field.ErrorList{
275 &field.Error{
276 Type: field.ErrorTypeInvalid,
277 Field: "scoringStrategy.resources[0].weight",
278 },
279 },
280 },
281 {
282
283
284
285
286
287
288 name: "bypass extended resource if the pod does not request",
289 requestedPod: st.MakePod().
290 Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
291 Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
292 Obj(),
293 nodes: []*v1.Node{
294 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
295 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000", v1.ResourceName(extendedRes): "4"}).Obj(),
296 },
297 resources: extendedResourceSet,
298 existingPods: nil,
299 expectedScores: []framework.NodeScore{{Name: "node1", Score: 50}, {Name: "node2", Score: 50}},
300 },
301 {
302
303
304
305
306
307
308
309
310 name: "honor extended resource if the pod request",
311 requestedPod: st.MakePod().Node("node1").
312 Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000", v1.ResourceName(extendedRes): "2"}).
313 Obj(),
314 nodes: []*v1.Node{
315 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000", v1.ResourceName(extendedRes): "4"}).Obj(),
316 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000", v1.ResourceName(extendedRes): "10"}).Obj(),
317 },
318 resources: extendedResourceSet,
319 existingPods: nil,
320 expectedScores: []framework.NodeScore{{Name: "node1", Score: 50}, {Name: "node2", Score: 40}},
321 },
322 {
323
324
325
326
327
328 name: "if the node doesn't have a resource",
329 requestedPod: st.MakePod().Node("node1").
330 Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "4000"}).
331 Obj(),
332 nodes: []*v1.Node{
333 st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
334 st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000", v1.ResourceName(extendedRes): "4"}).Obj(),
335 },
336 expectedScores: []framework.NodeScore{{Name: "node1", Score: 45}, {Name: "node2", Score: 45}},
337 resources: []config.ResourceSpec{
338 {Name: extendedRes, Weight: 2},
339 {Name: string(v1.ResourceCPU), Weight: 1},
340 {Name: string(v1.ResourceMemory), Weight: 1},
341 },
342 },
343 }
344
345 for _, test := range tests {
346 t.Run(test.name, func(t *testing.T) {
347 _, ctx := ktesting.NewTestContext(t)
348 ctx, cancel := context.WithCancel(ctx)
349 defer cancel()
350
351 state := framework.NewCycleState()
352 snapshot := cache.NewSnapshot(test.existingPods, test.nodes)
353 fh, _ := runtime.NewFramework(ctx, nil, nil, runtime.WithSnapshotSharedLister(snapshot))
354
355 p, err := NewFit(ctx,
356 &config.NodeResourcesFitArgs{
357 ScoringStrategy: &config.ScoringStrategy{
358 Type: config.MostAllocated,
359 Resources: test.resources,
360 },
361 }, fh, plfeature.Features{})
362
363 if diff := cmp.Diff(test.wantErrs.ToAggregate(), err, ignoreBadValueDetail); diff != "" {
364 t.Fatalf("got err (-want,+got):\n%s", diff)
365 }
366 if err != nil {
367 return
368 }
369
370 status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, tf.BuildNodeInfos(test.nodes))
371 if !status.IsSuccess() {
372 t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
373 }
374
375 var gotScores framework.NodeScoreList
376 for _, n := range test.nodes {
377 score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
378 if status.Code() != test.wantStatusCode {
379 t.Errorf("unexpected status code, want: %v, got: %v", test.wantStatusCode, status.Code())
380 }
381 gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
382 }
383
384 if diff := cmp.Diff(test.expectedScores, gotScores); diff != "" {
385 t.Errorf("Unexpected scores (-want,+got):\n%s", diff)
386 }
387 })
388 }
389 }
390
View as plain text