1
16
17 package runtime
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "reflect"
24 "strings"
25 "testing"
26 "time"
27
28 "github.com/google/go-cmp/cmp"
29 v1 "k8s.io/api/core/v1"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/runtime"
32 "k8s.io/apimachinery/pkg/types"
33 "k8s.io/apimachinery/pkg/util/sets"
34 "k8s.io/component-base/metrics/testutil"
35 "k8s.io/klog/v2/ktesting"
36 "k8s.io/kubernetes/pkg/scheduler/apis/config"
37 "k8s.io/kubernetes/pkg/scheduler/framework"
38 internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue"
39 "k8s.io/kubernetes/pkg/scheduler/metrics"
40 "k8s.io/utils/ptr"
41 )
42
43 const (
44 preEnqueuePlugin = "preEnqueue-plugin"
45 queueSortPlugin = "no-op-queue-sort-plugin"
46 scoreWithNormalizePlugin1 = "score-with-normalize-plugin-1"
47 scoreWithNormalizePlugin2 = "score-with-normalize-plugin-2"
48 scorePlugin1 = "score-plugin-1"
49 scorePlugin2 = "score-plugin-2"
50 pluginNotImplementingScore = "plugin-not-implementing-score"
51 preFilterPluginName = "prefilter-plugin"
52 preFilterWithExtensionsPluginName = "prefilter-with-extensions-plugin"
53 duplicatePluginName = "duplicate-plugin"
54 testPlugin = "test-plugin"
55 permitPlugin = "permit-plugin"
56 bindPlugin = "bind-plugin"
57 testCloseErrorPlugin = "test-close-error-plugin"
58
59 testProfileName = "test-profile"
60 testPercentageOfNodesToScore = 35
61 nodeName = "testNode"
62
63 injectReason = "injected status"
64 injectFilterReason = "injected filter status"
65 )
66
67
68
69 var _ framework.ScorePlugin = &TestScoreWithNormalizePlugin{}
70 var _ framework.ScorePlugin = &TestScorePlugin{}
71
72 var cmpOpts = []cmp.Option{
73 cmp.Comparer(func(s1 *framework.Status, s2 *framework.Status) bool {
74 if s1 == nil || s2 == nil {
75 return s1.IsSuccess() && s2.IsSuccess()
76 }
77 return s1.Code() == s2.Code() && s1.Plugin() == s2.Plugin() && s1.Message() == s2.Message()
78 }),
79 }
80
81 func newScoreWithNormalizePlugin1(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
82 var inj injectedResult
83 if err := DecodeInto(injArgs, &inj); err != nil {
84 return nil, err
85 }
86 return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin1, inj}, nil
87 }
88
89 func newScoreWithNormalizePlugin2(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
90 var inj injectedResult
91 if err := DecodeInto(injArgs, &inj); err != nil {
92 return nil, err
93 }
94 return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin2, inj}, nil
95 }
96
97 func newScorePlugin1(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
98 var inj injectedResult
99 if err := DecodeInto(injArgs, &inj); err != nil {
100 return nil, err
101 }
102 return &TestScorePlugin{scorePlugin1, inj}, nil
103 }
104
105 func newScorePlugin2(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
106 var inj injectedResult
107 if err := DecodeInto(injArgs, &inj); err != nil {
108 return nil, err
109 }
110 return &TestScorePlugin{scorePlugin2, inj}, nil
111 }
112
113 func newPluginNotImplementingScore(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
114 return &PluginNotImplementingScore{}, nil
115 }
116
117 type TestScoreWithNormalizePlugin struct {
118 name string
119 inj injectedResult
120 }
121
122 func (pl *TestScoreWithNormalizePlugin) Name() string {
123 return pl.name
124 }
125
126 func (pl *TestScoreWithNormalizePlugin) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
127 return injectNormalizeRes(pl.inj, scores)
128 }
129
130 func (pl *TestScoreWithNormalizePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
131 return setScoreRes(pl.inj)
132 }
133
134 func (pl *TestScoreWithNormalizePlugin) ScoreExtensions() framework.ScoreExtensions {
135 return pl
136 }
137
138
139 type TestScorePlugin struct {
140 name string
141 inj injectedResult
142 }
143
144 func (pl *TestScorePlugin) Name() string {
145 return pl.name
146 }
147
148 func (pl *TestScorePlugin) PreScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodes []*framework.NodeInfo) *framework.Status {
149 return framework.NewStatus(framework.Code(pl.inj.PreScoreStatus), injectReason)
150 }
151
152 func (pl *TestScorePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
153 return setScoreRes(pl.inj)
154 }
155
156 func (pl *TestScorePlugin) ScoreExtensions() framework.ScoreExtensions {
157 return nil
158 }
159
160
161 type PluginNotImplementingScore struct{}
162
163 func (pl *PluginNotImplementingScore) Name() string {
164 return pluginNotImplementingScore
165 }
166
167 func newTestPlugin(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
168 return &TestPlugin{name: testPlugin}, nil
169 }
170
171
172 type TestPlugin struct {
173 name string
174 inj injectedResult
175 }
176
177 func (pl *TestPlugin) AddPod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod, podInfoToAdd *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
178 return framework.NewStatus(framework.Code(pl.inj.PreFilterAddPodStatus), injectReason)
179 }
180 func (pl *TestPlugin) RemovePod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod, podInfoToRemove *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
181 return framework.NewStatus(framework.Code(pl.inj.PreFilterRemovePodStatus), injectReason)
182 }
183
184 func (pl *TestPlugin) Name() string {
185 return pl.name
186 }
187
188 func (pl *TestPlugin) Less(*framework.QueuedPodInfo, *framework.QueuedPodInfo) bool {
189 return false
190 }
191
192 func (pl *TestPlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
193 return 0, framework.NewStatus(framework.Code(pl.inj.ScoreStatus), injectReason)
194 }
195
196 func (pl *TestPlugin) ScoreExtensions() framework.ScoreExtensions {
197 return nil
198 }
199
200 func (pl *TestPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
201 return pl.inj.PreFilterResult, framework.NewStatus(framework.Code(pl.inj.PreFilterStatus), injectReason)
202 }
203
204 func (pl *TestPlugin) PreFilterExtensions() framework.PreFilterExtensions {
205 return pl
206 }
207
208 func (pl *TestPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
209 return framework.NewStatus(framework.Code(pl.inj.FilterStatus), injectFilterReason)
210 }
211
212 func (pl *TestPlugin) PostFilter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ framework.NodeToStatusMap) (*framework.PostFilterResult, *framework.Status) {
213 return nil, framework.NewStatus(framework.Code(pl.inj.PostFilterStatus), injectReason)
214 }
215
216 func (pl *TestPlugin) PreScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodes []*framework.NodeInfo) *framework.Status {
217 return framework.NewStatus(framework.Code(pl.inj.PreScoreStatus), injectReason)
218 }
219
220 func (pl *TestPlugin) Reserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
221 return framework.NewStatus(framework.Code(pl.inj.ReserveStatus), injectReason)
222 }
223
224 func (pl *TestPlugin) Unreserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) {
225 }
226
227 func (pl *TestPlugin) PreBind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
228 return framework.NewStatus(framework.Code(pl.inj.PreBindStatus), injectReason)
229 }
230
231 func (pl *TestPlugin) PostBind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) {
232 }
233
234 func (pl *TestPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
235 return framework.NewStatus(framework.Code(pl.inj.PermitStatus), injectReason), time.Duration(0)
236 }
237
238 func (pl *TestPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
239 return framework.NewStatus(framework.Code(pl.inj.BindStatus), injectReason)
240 }
241
242 func newTestCloseErrorPlugin(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
243 return &TestCloseErrorPlugin{name: testCloseErrorPlugin}, nil
244 }
245
246
247 type TestCloseErrorPlugin struct {
248 name string
249 }
250
251 func (pl *TestCloseErrorPlugin) Name() string {
252 return pl.name
253 }
254
255 var errClose = errors.New("close err")
256
257 func (pl *TestCloseErrorPlugin) Close() error {
258 return errClose
259 }
260
261
262 type TestPreFilterPlugin struct {
263 PreFilterCalled int
264 }
265
266 func (pl *TestPreFilterPlugin) Name() string {
267 return preFilterPluginName
268 }
269
270 func (pl *TestPreFilterPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
271 pl.PreFilterCalled++
272 return nil, nil
273 }
274
275 func (pl *TestPreFilterPlugin) PreFilterExtensions() framework.PreFilterExtensions {
276 return nil
277 }
278
279
280 type TestPreFilterWithExtensionsPlugin struct {
281 PreFilterCalled int
282 AddCalled int
283 RemoveCalled int
284 }
285
286 func (pl *TestPreFilterWithExtensionsPlugin) Name() string {
287 return preFilterWithExtensionsPluginName
288 }
289
290 func (pl *TestPreFilterWithExtensionsPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
291 pl.PreFilterCalled++
292 return nil, nil
293 }
294
295 func (pl *TestPreFilterWithExtensionsPlugin) AddPod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod,
296 podInfoToAdd *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
297 pl.AddCalled++
298 return nil
299 }
300
301 func (pl *TestPreFilterWithExtensionsPlugin) RemovePod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod,
302 podInfoToRemove *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
303 pl.RemoveCalled++
304 return nil
305 }
306
307 func (pl *TestPreFilterWithExtensionsPlugin) PreFilterExtensions() framework.PreFilterExtensions {
308 return pl
309 }
310
311 type TestDuplicatePlugin struct {
312 }
313
314 func (dp *TestDuplicatePlugin) Name() string {
315 return duplicatePluginName
316 }
317
318 func (dp *TestDuplicatePlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
319 return nil, nil
320 }
321
322 func (dp *TestDuplicatePlugin) PreFilterExtensions() framework.PreFilterExtensions {
323 return nil
324 }
325
326 var _ framework.PreFilterPlugin = &TestDuplicatePlugin{}
327
328 func newDuplicatePlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
329 return &TestDuplicatePlugin{}, nil
330 }
331
332
333 type TestPermitPlugin struct {
334 PreFilterCalled int
335 }
336
337 func (pp *TestPermitPlugin) Name() string {
338 return permitPlugin
339 }
340 func (pp *TestPermitPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
341 return framework.NewStatus(framework.Wait), 10 * time.Second
342 }
343
344 var _ framework.PreEnqueuePlugin = &TestPreEnqueuePlugin{}
345
346 type TestPreEnqueuePlugin struct{}
347
348 func (pl *TestPreEnqueuePlugin) Name() string {
349 return preEnqueuePlugin
350 }
351
352 func (pl *TestPreEnqueuePlugin) PreEnqueue(ctx context.Context, p *v1.Pod) *framework.Status {
353 return nil
354 }
355
356 var _ framework.QueueSortPlugin = &TestQueueSortPlugin{}
357
358 func newQueueSortPlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
359 return &TestQueueSortPlugin{}, nil
360 }
361
362
363 type TestQueueSortPlugin struct{}
364
365 func (pl *TestQueueSortPlugin) Name() string {
366 return queueSortPlugin
367 }
368
369 func (pl *TestQueueSortPlugin) Less(_, _ *framework.QueuedPodInfo) bool {
370 return false
371 }
372
373 var _ framework.BindPlugin = &TestBindPlugin{}
374
375 func newBindPlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
376 return &TestBindPlugin{}, nil
377 }
378
379
380 type TestBindPlugin struct{}
381
382 func (t TestBindPlugin) Name() string {
383 return bindPlugin
384 }
385
386 func (t TestBindPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
387 return nil
388 }
389
390
391 var registry = func() Registry {
392 r := make(Registry)
393 r.Register(scoreWithNormalizePlugin1, newScoreWithNormalizePlugin1)
394 r.Register(scoreWithNormalizePlugin2, newScoreWithNormalizePlugin2)
395 r.Register(scorePlugin1, newScorePlugin1)
396 r.Register(scorePlugin2, newScorePlugin2)
397 r.Register(pluginNotImplementingScore, newPluginNotImplementingScore)
398 r.Register(duplicatePluginName, newDuplicatePlugin)
399 r.Register(testPlugin, newTestPlugin)
400 r.Register(queueSortPlugin, newQueueSortPlugin)
401 r.Register(bindPlugin, newBindPlugin)
402 r.Register(testCloseErrorPlugin, newTestCloseErrorPlugin)
403 return r
404 }()
405
406 var defaultWeights = map[string]int32{
407 scoreWithNormalizePlugin1: 1,
408 scoreWithNormalizePlugin2: 2,
409 scorePlugin1: 1,
410 }
411
412 var state = &framework.CycleState{}
413
414
415 var pod = &v1.Pod{}
416 var node = &v1.Node{
417 ObjectMeta: metav1.ObjectMeta{
418 Name: nodeName,
419 },
420 }
421 var lowPriority, highPriority = int32(0), int32(1000)
422 var lowPriorityPod = &v1.Pod{
423 ObjectMeta: metav1.ObjectMeta{UID: "low"},
424 Spec: v1.PodSpec{Priority: &lowPriority},
425 }
426 var highPriorityPod = &v1.Pod{
427 ObjectMeta: metav1.ObjectMeta{UID: "high"},
428 Spec: v1.PodSpec{Priority: &highPriority},
429 }
430 var nodes = []*v1.Node{
431 {ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
432 {ObjectMeta: metav1.ObjectMeta{Name: "node2"}},
433 }
434
435 var (
436 errInjectedStatus = errors.New(injectReason)
437 errInjectedFilterStatus = errors.New(injectFilterReason)
438 )
439
440 func newFrameworkWithQueueSortAndBind(ctx context.Context, r Registry, profile config.KubeSchedulerProfile, opts ...Option) (framework.Framework, error) {
441 if _, ok := r[queueSortPlugin]; !ok {
442 r[queueSortPlugin] = newQueueSortPlugin
443 }
444 if _, ok := r[bindPlugin]; !ok {
445 r[bindPlugin] = newBindPlugin
446 }
447
448 if len(profile.Plugins.QueueSort.Enabled) == 0 {
449 profile.Plugins.QueueSort.Enabled = append(profile.Plugins.QueueSort.Enabled, config.Plugin{Name: queueSortPlugin})
450 }
451 if len(profile.Plugins.Bind.Enabled) == 0 {
452 profile.Plugins.Bind.Enabled = append(profile.Plugins.Bind.Enabled, config.Plugin{Name: bindPlugin})
453 }
454 return NewFramework(ctx, r, &profile, opts...)
455 }
456
457 func TestInitFrameworkWithScorePlugins(t *testing.T) {
458 tests := []struct {
459 name string
460 plugins *config.Plugins
461
462 initErr bool
463 }{
464 {
465 name: "enabled Score plugin doesn't exist in registry",
466 plugins: buildScoreConfigDefaultWeights("notExist"),
467 initErr: true,
468 },
469 {
470 name: "enabled Score plugin doesn't extend the ScorePlugin interface",
471 plugins: buildScoreConfigDefaultWeights(pluginNotImplementingScore),
472 initErr: true,
473 },
474 {
475 name: "Score plugins are nil",
476 plugins: &config.Plugins{},
477 },
478 {
479 name: "enabled Score plugin list is empty",
480 plugins: buildScoreConfigDefaultWeights(),
481 },
482 {
483 name: "enabled plugin only implements ScorePlugin interface",
484 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
485 },
486 {
487 name: "enabled plugin implements ScoreWithNormalizePlugin interface",
488 plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
489 },
490 }
491
492 for _, tt := range tests {
493 t.Run(tt.name, func(t *testing.T) {
494 profile := config.KubeSchedulerProfile{Plugins: tt.plugins}
495 _, ctx := ktesting.NewTestContext(t)
496 ctx, cancel := context.WithCancel(ctx)
497 defer cancel()
498 _, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
499 if tt.initErr && err == nil {
500 t.Fatal("Framework initialization should fail")
501 }
502 if !tt.initErr && err != nil {
503 t.Fatalf("Failed to create framework for testing: %v", err)
504 }
505 })
506 }
507 }
508
509 func TestNewFrameworkErrors(t *testing.T) {
510 tests := []struct {
511 name string
512 plugins *config.Plugins
513 pluginCfg []config.PluginConfig
514 wantErr string
515 }{
516 {
517 name: "duplicate plugin name",
518 plugins: &config.Plugins{
519 PreFilter: config.PluginSet{
520 Enabled: []config.Plugin{
521 {Name: duplicatePluginName, Weight: 1},
522 {Name: duplicatePluginName, Weight: 1},
523 },
524 },
525 },
526 pluginCfg: []config.PluginConfig{
527 {Name: duplicatePluginName},
528 },
529 wantErr: "already registered",
530 },
531 {
532 name: "duplicate plugin config",
533 plugins: &config.Plugins{
534 PreFilter: config.PluginSet{
535 Enabled: []config.Plugin{
536 {Name: duplicatePluginName, Weight: 1},
537 },
538 },
539 },
540 pluginCfg: []config.PluginConfig{
541 {Name: duplicatePluginName},
542 {Name: duplicatePluginName},
543 },
544 wantErr: "repeated config for plugin",
545 },
546 }
547
548 for _, tc := range tests {
549 t.Run(tc.name, func(t *testing.T) {
550 _, ctx := ktesting.NewTestContext(t)
551 ctx, cancel := context.WithCancel(ctx)
552 defer cancel()
553 profile := &config.KubeSchedulerProfile{
554 Plugins: tc.plugins,
555 PluginConfig: tc.pluginCfg,
556 }
557 _, err := NewFramework(ctx, registry, profile)
558 if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
559 t.Errorf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
560 }
561 })
562 }
563 }
564
565 func TestNewFrameworkMultiPointExpansion(t *testing.T) {
566 tests := []struct {
567 name string
568 plugins *config.Plugins
569 wantPlugins *config.Plugins
570 wantErr string
571 }{
572 {
573 name: "plugin expansion",
574 plugins: &config.Plugins{
575 MultiPoint: config.PluginSet{
576 Enabled: []config.Plugin{
577 {Name: testPlugin, Weight: 5},
578 },
579 },
580 },
581 wantPlugins: &config.Plugins{
582 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
583 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
584 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
585 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
586 PreScore: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
587 Score: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 5}}},
588 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
589 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
590 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
591 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
592 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
593 },
594 },
595 {
596 name: "disable MultiPoint plugin at some extension points",
597 plugins: &config.Plugins{
598 MultiPoint: config.PluginSet{
599 Enabled: []config.Plugin{
600 {Name: testPlugin},
601 },
602 },
603 PreScore: config.PluginSet{
604 Disabled: []config.Plugin{
605 {Name: testPlugin},
606 },
607 },
608 Score: config.PluginSet{
609 Disabled: []config.Plugin{
610 {Name: testPlugin},
611 },
612 },
613 },
614 wantPlugins: &config.Plugins{
615 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
616 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
617 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
618 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
619 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
620 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
621 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
622 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
623 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
624 },
625 },
626 {
627 name: "Multiple MultiPoint plugins",
628 plugins: &config.Plugins{
629 MultiPoint: config.PluginSet{
630 Enabled: []config.Plugin{
631 {Name: testPlugin},
632 {Name: scorePlugin1},
633 },
634 },
635 },
636 wantPlugins: &config.Plugins{
637 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
638 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
639 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
640 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
641 PreScore: config.PluginSet{Enabled: []config.Plugin{
642 {Name: testPlugin},
643 {Name: scorePlugin1},
644 }},
645 Score: config.PluginSet{Enabled: []config.Plugin{
646 {Name: testPlugin, Weight: 1},
647 {Name: scorePlugin1, Weight: 1},
648 }},
649 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
650 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
651 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
652 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
653 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
654 },
655 },
656 {
657 name: "disable MultiPoint extension",
658 plugins: &config.Plugins{
659 MultiPoint: config.PluginSet{
660 Enabled: []config.Plugin{
661 {Name: testPlugin},
662 {Name: scorePlugin1},
663 },
664 },
665 PreScore: config.PluginSet{
666 Disabled: []config.Plugin{
667 {Name: "*"},
668 },
669 },
670 },
671 wantPlugins: &config.Plugins{
672 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
673 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
674 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
675 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
676 Score: config.PluginSet{Enabled: []config.Plugin{
677 {Name: testPlugin, Weight: 1},
678 {Name: scorePlugin1, Weight: 1},
679 }},
680 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
681 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
682 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
683 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
684 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
685 },
686 },
687 {
688 name: "Reorder MultiPoint plugins (specified extension takes precedence)",
689 plugins: &config.Plugins{
690 MultiPoint: config.PluginSet{
691 Enabled: []config.Plugin{
692 {Name: scoreWithNormalizePlugin1},
693 {Name: testPlugin},
694 {Name: scorePlugin1},
695 },
696 },
697 Score: config.PluginSet{
698 Enabled: []config.Plugin{
699 {Name: scorePlugin1},
700 {Name: testPlugin},
701 },
702 },
703 },
704 wantPlugins: &config.Plugins{
705 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
706 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
707 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
708 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
709 PreScore: config.PluginSet{Enabled: []config.Plugin{
710 {Name: testPlugin},
711 {Name: scorePlugin1},
712 }},
713 Score: config.PluginSet{Enabled: []config.Plugin{
714 {Name: scorePlugin1, Weight: 1},
715 {Name: testPlugin, Weight: 1},
716 {Name: scoreWithNormalizePlugin1, Weight: 1},
717 }},
718 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
719 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
720 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
721 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
722 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
723 },
724 },
725 {
726 name: "Reorder MultiPoint plugins (specified extension only takes precedence when it exists in MultiPoint)",
727 plugins: &config.Plugins{
728 MultiPoint: config.PluginSet{
729 Enabled: []config.Plugin{
730 {Name: testPlugin},
731 {Name: scorePlugin1},
732 },
733 },
734 Score: config.PluginSet{
735 Enabled: []config.Plugin{
736 {Name: scoreWithNormalizePlugin1},
737 {Name: scorePlugin1},
738 {Name: testPlugin},
739 },
740 },
741 },
742 wantPlugins: &config.Plugins{
743 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
744 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
745 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
746 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
747 PreScore: config.PluginSet{Enabled: []config.Plugin{
748 {Name: testPlugin},
749 {Name: scorePlugin1},
750 }},
751 Score: config.PluginSet{Enabled: []config.Plugin{
752 {Name: scorePlugin1, Weight: 1},
753 {Name: testPlugin, Weight: 1},
754 {Name: scoreWithNormalizePlugin1, Weight: 1},
755 }},
756 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
757 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
758 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
759 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
760 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
761 },
762 },
763 {
764 name: "Override MultiPoint plugins weights",
765 plugins: &config.Plugins{
766 MultiPoint: config.PluginSet{
767 Enabled: []config.Plugin{
768 {Name: testPlugin},
769 {Name: scorePlugin1},
770 },
771 },
772 Score: config.PluginSet{
773 Enabled: []config.Plugin{
774 {Name: scorePlugin1, Weight: 5},
775 {Name: testPlugin, Weight: 3},
776 },
777 },
778 },
779 wantPlugins: &config.Plugins{
780 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
781 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
782 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
783 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
784 PreScore: config.PluginSet{Enabled: []config.Plugin{
785 {Name: testPlugin},
786 {Name: scorePlugin1},
787 }},
788 Score: config.PluginSet{Enabled: []config.Plugin{
789 {Name: scorePlugin1, Weight: 5},
790 {Name: testPlugin, Weight: 3},
791 }},
792 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
793 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
794 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
795 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
796 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
797 },
798 },
799 {
800 name: "disable and enable MultiPoint plugins with '*'",
801 plugins: &config.Plugins{
802 MultiPoint: config.PluginSet{
803 Enabled: []config.Plugin{
804 {Name: queueSortPlugin},
805 {Name: bindPlugin},
806 {Name: scorePlugin1},
807 },
808 Disabled: []config.Plugin{
809 {Name: "*"},
810 },
811 },
812 },
813 wantPlugins: &config.Plugins{
814 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
815 PreScore: config.PluginSet{Enabled: []config.Plugin{
816 {Name: scorePlugin1},
817 }},
818 Score: config.PluginSet{Enabled: []config.Plugin{
819 {Name: scorePlugin1, Weight: 1},
820 }},
821 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
822 },
823 },
824 {
825 name: "disable and enable MultiPoint plugin by name",
826 plugins: &config.Plugins{
827 MultiPoint: config.PluginSet{
828 Enabled: []config.Plugin{
829 {Name: bindPlugin},
830 {Name: queueSortPlugin},
831 {Name: scorePlugin1},
832 },
833 Disabled: []config.Plugin{
834 {Name: scorePlugin1},
835 },
836 },
837 },
838 wantPlugins: &config.Plugins{
839 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
840 PreScore: config.PluginSet{Enabled: []config.Plugin{
841 {Name: scorePlugin1},
842 }},
843 Score: config.PluginSet{Enabled: []config.Plugin{
844 {Name: scorePlugin1, Weight: 1},
845 }},
846 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
847 },
848 },
849 {
850 name: "Expect 'already registered' error",
851 plugins: &config.Plugins{
852 MultiPoint: config.PluginSet{
853 Enabled: []config.Plugin{
854 {Name: testPlugin},
855 {Name: testPlugin},
856 },
857 },
858 },
859 wantErr: "already registered",
860 },
861 {
862 name: "Override MultiPoint plugins weights and avoid a plugin being loaded multiple times",
863 plugins: &config.Plugins{
864 MultiPoint: config.PluginSet{
865 Enabled: []config.Plugin{
866 {Name: testPlugin},
867 {Name: scorePlugin1},
868 },
869 },
870 Score: config.PluginSet{
871 Enabled: []config.Plugin{
872 {Name: scorePlugin1, Weight: 5},
873 {Name: scorePlugin2, Weight: 5},
874 {Name: testPlugin, Weight: 3},
875 },
876 },
877 },
878 wantPlugins: &config.Plugins{
879 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
880 PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
881 Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
882 PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
883 PreScore: config.PluginSet{Enabled: []config.Plugin{
884 {Name: testPlugin},
885 {Name: scorePlugin1},
886 }},
887 Score: config.PluginSet{Enabled: []config.Plugin{
888 {Name: scorePlugin1, Weight: 5},
889 {Name: testPlugin, Weight: 3},
890 {Name: scorePlugin2, Weight: 5},
891 }},
892 Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
893 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
894 PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
895 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
896 PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
897 },
898 },
899 }
900
901 for _, tc := range tests {
902 t.Run(tc.name, func(t *testing.T) {
903 _, ctx := ktesting.NewTestContext(t)
904 ctx, cancel := context.WithCancel(ctx)
905 defer cancel()
906 fw, err := NewFramework(ctx, registry, &config.KubeSchedulerProfile{Plugins: tc.plugins})
907 if err != nil {
908 if tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr) {
909 t.Fatalf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
910 }
911 } else {
912 if tc.wantErr != "" {
913 t.Fatalf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
914 }
915 }
916
917 if tc.wantErr == "" {
918 if diff := cmp.Diff(tc.wantPlugins, fw.ListPlugins()); diff != "" {
919 t.Fatalf("Unexpected eventToPlugin map (-want,+got):%s", diff)
920 }
921 }
922 })
923 }
924 }
925
926 func TestPreEnqueuePlugins(t *testing.T) {
927 tests := []struct {
928 name string
929 plugins []framework.Plugin
930 want []framework.PreEnqueuePlugin
931 }{
932 {
933 name: "no PreEnqueuePlugin registered",
934 },
935 {
936 name: "one PreEnqueuePlugin registered",
937 plugins: []framework.Plugin{
938 &TestPreEnqueuePlugin{},
939 },
940 want: []framework.PreEnqueuePlugin{
941 &TestPreEnqueuePlugin{},
942 },
943 },
944 }
945
946 for _, tt := range tests {
947 t.Run(tt.name, func(t *testing.T) {
948 registry := Registry{}
949 cfgPls := &config.Plugins{}
950 for _, pl := range tt.plugins {
951
952 tmpPl := pl
953 if err := registry.Register(pl.Name(),
954 func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
955 return tmpPl, nil
956 }); err != nil {
957 t.Fatalf("fail to register preEnqueue plugin (%s)", pl.Name())
958 }
959
960 cfgPls.PreEnqueue.Enabled = append(
961 cfgPls.PreEnqueue.Enabled,
962 config.Plugin{Name: pl.Name()},
963 )
964 }
965 profile := config.KubeSchedulerProfile{Plugins: cfgPls}
966 ctx, cancel := context.WithCancel(context.Background())
967 defer cancel()
968 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
969 if err != nil {
970 t.Fatalf("fail to create framework: %s", err)
971 }
972
973 got := f.PreEnqueuePlugins()
974 if !reflect.DeepEqual(got, tt.want) {
975 t.Errorf("PreEnqueuePlugins(): want %v, but got %v", tt.want, got)
976 }
977 })
978 }
979 }
980
981 func TestRunPreScorePlugins(t *testing.T) {
982 tests := []struct {
983 name string
984 plugins []*TestPlugin
985 wantSkippedPlugins sets.Set[string]
986 wantStatusCode framework.Code
987 }{
988 {
989 name: "all PreScorePlugins returned success",
990 plugins: []*TestPlugin{
991 {
992 name: "success1",
993 },
994 {
995 name: "success2",
996 },
997 },
998 wantStatusCode: framework.Success,
999 },
1000 {
1001 name: "one PreScore plugin returned success, but another PreScore plugin returned non-success",
1002 plugins: []*TestPlugin{
1003 {
1004 name: "success",
1005 },
1006 {
1007 name: "error",
1008 inj: injectedResult{PreScoreStatus: int(framework.Error)},
1009 },
1010 },
1011 wantStatusCode: framework.Error,
1012 },
1013 {
1014 name: "one PreScore plugin returned skip, but another PreScore plugin returned non-success",
1015 plugins: []*TestPlugin{
1016 {
1017 name: "skip",
1018 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1019 },
1020 {
1021 name: "error",
1022 inj: injectedResult{PreScoreStatus: int(framework.Error)},
1023 },
1024 },
1025 wantSkippedPlugins: sets.New("skip"),
1026 wantStatusCode: framework.Error,
1027 },
1028 {
1029 name: "all PreScore plugins returned skip",
1030 plugins: []*TestPlugin{
1031 {
1032 name: "skip1",
1033 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1034 },
1035 {
1036 name: "skip2",
1037 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1038 },
1039 {
1040 name: "skip3",
1041 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1042 },
1043 },
1044 wantSkippedPlugins: sets.New("skip1", "skip2", "skip3"),
1045 wantStatusCode: framework.Success,
1046 },
1047 {
1048 name: "some PreScore plugins returned skip",
1049 plugins: []*TestPlugin{
1050 {
1051 name: "skip1",
1052 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1053 },
1054 {
1055 name: "success1",
1056 },
1057 {
1058 name: "skip2",
1059 inj: injectedResult{PreScoreStatus: int(framework.Skip)},
1060 },
1061 {
1062 name: "success2",
1063 },
1064 },
1065 wantSkippedPlugins: sets.New("skip1", "skip2"),
1066 wantStatusCode: framework.Success,
1067 },
1068 }
1069
1070 for _, tt := range tests {
1071 t.Run(tt.name, func(t *testing.T) {
1072 r := make(Registry)
1073 enabled := make([]config.Plugin, len(tt.plugins))
1074 for i, p := range tt.plugins {
1075 p := p
1076 enabled[i].Name = p.name
1077 if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1078 return p, nil
1079 }); err != nil {
1080 t.Fatalf("fail to register PreScorePlugins plugin (%s)", p.Name())
1081 }
1082 }
1083
1084 ctx, cancel := context.WithCancel(context.Background())
1085 defer cancel()
1086
1087 f, err := newFrameworkWithQueueSortAndBind(
1088 ctx,
1089 r,
1090 config.KubeSchedulerProfile{Plugins: &config.Plugins{PreScore: config.PluginSet{Enabled: enabled}}},
1091 )
1092 if err != nil {
1093 t.Fatalf("Failed to create framework for testing: %v", err)
1094 }
1095
1096 state := framework.NewCycleState()
1097 status := f.RunPreScorePlugins(ctx, state, nil, nil)
1098 if status.Code() != tt.wantStatusCode {
1099 t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
1100 }
1101 skipped := state.SkipScorePlugins
1102 if d := cmp.Diff(skipped, tt.wantSkippedPlugins); d != "" {
1103 t.Errorf("wrong skip score plugins. got: %v, want: %v, diff: %s", skipped, tt.wantSkippedPlugins, d)
1104 }
1105 })
1106 }
1107 }
1108
1109 func TestRunScorePlugins(t *testing.T) {
1110 tests := []struct {
1111 name string
1112 registry Registry
1113 plugins *config.Plugins
1114 pluginConfigs []config.PluginConfig
1115 want []framework.NodePluginScores
1116 skippedPlugins sets.Set[string]
1117
1118 err bool
1119 }{
1120 {
1121 name: "no Score plugins",
1122 plugins: buildScoreConfigDefaultWeights(),
1123 want: []framework.NodePluginScores{
1124 {
1125 Name: "node1",
1126 Scores: []framework.PluginScore{},
1127 },
1128 {
1129 Name: "node2",
1130 Scores: []framework.PluginScore{},
1131 },
1132 },
1133 },
1134 {
1135 name: "single Score plugin",
1136 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
1137 pluginConfigs: []config.PluginConfig{
1138 {
1139 Name: scorePlugin1,
1140 Args: &runtime.Unknown{
1141 Raw: []byte(`{ "scoreRes": 1 }`),
1142 },
1143 },
1144 },
1145
1146 want: []framework.NodePluginScores{
1147 {
1148 Name: "node1",
1149 Scores: []framework.PluginScore{
1150 {
1151 Name: scorePlugin1,
1152 Score: 1,
1153 },
1154 },
1155 TotalScore: 1,
1156 },
1157 {
1158 Name: "node2",
1159 Scores: []framework.PluginScore{
1160 {
1161 Name: scorePlugin1,
1162 Score: 1,
1163 },
1164 },
1165 TotalScore: 1,
1166 },
1167 },
1168 },
1169 {
1170 name: "single ScoreWithNormalize plugin",
1171
1172 plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
1173 pluginConfigs: []config.PluginConfig{
1174 {
1175 Name: scoreWithNormalizePlugin1,
1176 Args: &runtime.Unknown{
1177 Raw: []byte(`{ "scoreRes": 10, "normalizeRes": 5 }`),
1178 },
1179 },
1180 },
1181
1182 want: []framework.NodePluginScores{
1183 {
1184 Name: "node1",
1185 Scores: []framework.PluginScore{
1186 {
1187 Name: scoreWithNormalizePlugin1,
1188 Score: 5,
1189 },
1190 },
1191 TotalScore: 5,
1192 },
1193 {
1194 Name: "node2",
1195 Scores: []framework.PluginScore{
1196 {
1197 Name: scoreWithNormalizePlugin1,
1198 Score: 5,
1199 },
1200 },
1201 TotalScore: 5,
1202 },
1203 },
1204 },
1205 {
1206 name: "3 Score plugins, 2 NormalizeScore plugins",
1207 plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1, scoreWithNormalizePlugin2),
1208 pluginConfigs: []config.PluginConfig{
1209 {
1210 Name: scorePlugin1,
1211 Args: &runtime.Unknown{
1212 Raw: []byte(`{ "scoreRes": 1 }`),
1213 },
1214 },
1215 {
1216 Name: scoreWithNormalizePlugin1,
1217 Args: &runtime.Unknown{
1218 Raw: []byte(`{ "scoreRes": 3, "normalizeRes": 4}`),
1219 },
1220 },
1221 {
1222 Name: scoreWithNormalizePlugin2,
1223 Args: &runtime.Unknown{
1224 Raw: []byte(`{ "scoreRes": 4, "normalizeRes": 5}`),
1225 },
1226 },
1227 },
1228
1229
1230
1231 want: []framework.NodePluginScores{
1232 {
1233 Name: "node1",
1234 Scores: []framework.PluginScore{
1235 {
1236 Name: scorePlugin1,
1237 Score: 1,
1238 },
1239 {
1240 Name: scoreWithNormalizePlugin1,
1241 Score: 4,
1242 },
1243 {
1244 Name: scoreWithNormalizePlugin2,
1245 Score: 10,
1246 },
1247 },
1248 TotalScore: 15,
1249 },
1250 {
1251 Name: "node2",
1252 Scores: []framework.PluginScore{
1253 {
1254 Name: scorePlugin1,
1255 Score: 1,
1256 },
1257 {
1258 Name: scoreWithNormalizePlugin1,
1259 Score: 4,
1260 },
1261 {
1262 Name: scoreWithNormalizePlugin2,
1263 Score: 10,
1264 },
1265 },
1266 TotalScore: 15,
1267 },
1268 },
1269 },
1270 {
1271 name: "score fails",
1272 pluginConfigs: []config.PluginConfig{
1273 {
1274 Name: scoreWithNormalizePlugin1,
1275 Args: &runtime.Unknown{
1276 Raw: []byte(`{ "scoreStatus": 1 }`),
1277 },
1278 },
1279 },
1280 plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
1281 err: true,
1282 },
1283 {
1284 name: "normalize fails",
1285 pluginConfigs: []config.PluginConfig{
1286 {
1287 Name: scoreWithNormalizePlugin1,
1288 Args: &runtime.Unknown{
1289 Raw: []byte(`{ "normalizeStatus": 1 }`),
1290 },
1291 },
1292 },
1293 plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
1294 err: true,
1295 },
1296 {
1297 name: "Score plugin return score greater than MaxNodeScore",
1298 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
1299 pluginConfigs: []config.PluginConfig{
1300 {
1301 Name: scorePlugin1,
1302 Args: &runtime.Unknown{
1303 Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, framework.MaxNodeScore+1)),
1304 },
1305 },
1306 },
1307 err: true,
1308 },
1309 {
1310 name: "Score plugin return score less than MinNodeScore",
1311 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
1312 pluginConfigs: []config.PluginConfig{
1313 {
1314 Name: scorePlugin1,
1315 Args: &runtime.Unknown{
1316 Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, framework.MinNodeScore-1)),
1317 },
1318 },
1319 },
1320 err: true,
1321 },
1322 {
1323 name: "ScoreWithNormalize plugin return score greater than MaxNodeScore",
1324 plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
1325 pluginConfigs: []config.PluginConfig{
1326 {
1327 Name: scoreWithNormalizePlugin1,
1328 Args: &runtime.Unknown{
1329 Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, framework.MaxNodeScore+1)),
1330 },
1331 },
1332 },
1333 err: true,
1334 },
1335 {
1336 name: "ScoreWithNormalize plugin return score less than MinNodeScore",
1337 plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
1338 pluginConfigs: []config.PluginConfig{
1339 {
1340 Name: scoreWithNormalizePlugin1,
1341 Args: &runtime.Unknown{
1342 Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, framework.MinNodeScore-1)),
1343 },
1344 },
1345 },
1346 err: true,
1347 },
1348 {
1349 name: "single Score plugin with MultiPointExpansion",
1350 plugins: &config.Plugins{
1351 MultiPoint: config.PluginSet{
1352 Enabled: []config.Plugin{
1353 {Name: scorePlugin1},
1354 },
1355 },
1356 Score: config.PluginSet{
1357 Enabled: []config.Plugin{
1358 {Name: scorePlugin1, Weight: 3},
1359 },
1360 },
1361 },
1362 pluginConfigs: []config.PluginConfig{
1363 {
1364 Name: scorePlugin1,
1365 Args: &runtime.Unknown{
1366 Raw: []byte(`{ "scoreRes": 1 }`),
1367 },
1368 },
1369 },
1370
1371 want: []framework.NodePluginScores{
1372 {
1373 Name: "node1",
1374 Scores: []framework.PluginScore{
1375 {
1376 Name: scorePlugin1,
1377 Score: 3,
1378 },
1379 },
1380 TotalScore: 3,
1381 },
1382 {
1383 Name: "node2",
1384 Scores: []framework.PluginScore{
1385 {
1386 Name: scorePlugin1,
1387 Score: 3,
1388 },
1389 },
1390 TotalScore: 3,
1391 },
1392 },
1393 },
1394 {
1395 name: "one success plugin, one skip plugin",
1396 plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
1397 pluginConfigs: []config.PluginConfig{
1398 {
1399 Name: scorePlugin1,
1400 Args: &runtime.Unknown{
1401 Raw: []byte(`{ "scoreRes": 1 }`),
1402 },
1403 },
1404 {
1405 Name: scoreWithNormalizePlugin1,
1406 Args: &runtime.Unknown{
1407 Raw: []byte(`{ "scoreStatus": 1 }`),
1408 },
1409 },
1410 },
1411 skippedPlugins: sets.New(scoreWithNormalizePlugin1),
1412 want: []framework.NodePluginScores{
1413 {
1414 Name: "node1",
1415 Scores: []framework.PluginScore{
1416 {
1417 Name: scorePlugin1,
1418 Score: 1,
1419 },
1420 },
1421 TotalScore: 1,
1422 },
1423 {
1424 Name: "node2",
1425 Scores: []framework.PluginScore{
1426 {
1427 Name: scorePlugin1,
1428 Score: 1,
1429 },
1430 },
1431 TotalScore: 1,
1432 },
1433 },
1434 },
1435 {
1436 name: "all plugins are skipped in prescore",
1437 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
1438 pluginConfigs: []config.PluginConfig{
1439 {
1440 Name: scorePlugin1,
1441 Args: &runtime.Unknown{
1442 Raw: []byte(`{ "scoreStatus": 1 }`),
1443 },
1444 },
1445 },
1446 skippedPlugins: sets.New(scorePlugin1),
1447 want: []framework.NodePluginScores{
1448 {
1449 Name: "node1",
1450 Scores: []framework.PluginScore{},
1451 },
1452 {
1453 Name: "node2",
1454 Scores: []framework.PluginScore{},
1455 },
1456 },
1457 },
1458 {
1459 name: "skipped prescore plugin number greater than the number of score plugins",
1460 plugins: buildScoreConfigDefaultWeights(scorePlugin1),
1461 pluginConfigs: nil,
1462 skippedPlugins: sets.New(scorePlugin1, "score-plugin-unknown"),
1463 want: []framework.NodePluginScores{
1464 {
1465 Name: "node1",
1466 Scores: []framework.PluginScore{},
1467 },
1468 {
1469 Name: "node2",
1470 Scores: []framework.PluginScore{},
1471 },
1472 },
1473 },
1474 }
1475
1476 for _, tt := range tests {
1477 t.Run(tt.name, func(t *testing.T) {
1478
1479 profile := config.KubeSchedulerProfile{
1480 Plugins: tt.plugins,
1481 PluginConfig: tt.pluginConfigs,
1482 }
1483 ctx, cancel := context.WithCancel(context.Background())
1484 defer cancel()
1485 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
1486 if err != nil {
1487 t.Fatalf("Failed to create framework for testing: %v", err)
1488 }
1489
1490 state := framework.NewCycleState()
1491 state.SkipScorePlugins = tt.skippedPlugins
1492 res, status := f.RunScorePlugins(ctx, state, pod, BuildNodeInfos(nodes))
1493
1494 if tt.err {
1495 if status.IsSuccess() {
1496 t.Errorf("Expected status to be non-success. got: %v", status.Code().String())
1497 }
1498 return
1499 }
1500
1501 if !status.IsSuccess() {
1502 t.Errorf("Expected status to be success.")
1503 }
1504 if !reflect.DeepEqual(res, tt.want) {
1505 t.Errorf("Score map after RunScorePlugin. got: %+v, want: %+v.", res, tt.want)
1506 }
1507 })
1508 }
1509 }
1510
1511 func TestPreFilterPlugins(t *testing.T) {
1512 preFilter1 := &TestPreFilterPlugin{}
1513 preFilter2 := &TestPreFilterWithExtensionsPlugin{}
1514 r := make(Registry)
1515 r.Register(preFilterPluginName,
1516 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1517 return preFilter1, nil
1518 })
1519 r.Register(preFilterWithExtensionsPluginName,
1520 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1521 return preFilter2, nil
1522 })
1523 plugins := &config.Plugins{PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: preFilterWithExtensionsPluginName}, {Name: preFilterPluginName}}}}
1524 t.Run("TestPreFilterPlugin", func(t *testing.T) {
1525 profile := config.KubeSchedulerProfile{Plugins: plugins}
1526 ctx, cancel := context.WithCancel(context.Background())
1527 defer cancel()
1528
1529 f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
1530 if err != nil {
1531 t.Fatalf("Failed to create framework for testing: %v", err)
1532 }
1533 state := framework.NewCycleState()
1534
1535 f.RunPreFilterPlugins(ctx, state, nil)
1536 f.RunPreFilterExtensionAddPod(ctx, state, nil, nil, nil)
1537 f.RunPreFilterExtensionRemovePod(ctx, state, nil, nil, nil)
1538
1539 if preFilter1.PreFilterCalled != 1 {
1540 t.Errorf("preFilter1 called %v, expected: 1", preFilter1.PreFilterCalled)
1541 }
1542 if preFilter2.PreFilterCalled != 1 {
1543 t.Errorf("preFilter2 called %v, expected: 1", preFilter2.PreFilterCalled)
1544 }
1545 if preFilter2.AddCalled != 1 {
1546 t.Errorf("AddPod called %v, expected: 1", preFilter2.AddCalled)
1547 }
1548 if preFilter2.RemoveCalled != 1 {
1549 t.Errorf("AddPod called %v, expected: 1", preFilter2.RemoveCalled)
1550 }
1551 })
1552 }
1553
1554 func TestRunPreFilterPlugins(t *testing.T) {
1555 tests := []struct {
1556 name string
1557 plugins []*TestPlugin
1558 wantPreFilterResult *framework.PreFilterResult
1559 wantSkippedPlugins sets.Set[string]
1560 wantStatusCode framework.Code
1561 }{
1562 {
1563 name: "all PreFilter returned success",
1564 plugins: []*TestPlugin{
1565 {
1566 name: "success1",
1567 },
1568 {
1569 name: "success2",
1570 },
1571 },
1572 wantPreFilterResult: nil,
1573 wantStatusCode: framework.Success,
1574 },
1575 {
1576 name: "one PreFilter plugin returned success, but another PreFilter plugin returned non-success",
1577 plugins: []*TestPlugin{
1578 {
1579 name: "success",
1580 },
1581 {
1582 name: "error",
1583 inj: injectedResult{PreFilterStatus: int(framework.Error)},
1584 },
1585 },
1586 wantPreFilterResult: nil,
1587 wantStatusCode: framework.Error,
1588 },
1589 {
1590 name: "one PreFilter plugin returned skip, but another PreFilter plugin returned non-success",
1591 plugins: []*TestPlugin{
1592 {
1593 name: "skip",
1594 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1595 },
1596 {
1597 name: "error",
1598 inj: injectedResult{PreFilterStatus: int(framework.Error)},
1599 },
1600 },
1601 wantSkippedPlugins: sets.New("skip"),
1602 wantStatusCode: framework.Error,
1603 },
1604 {
1605 name: "all PreFilter plugins returned skip",
1606 plugins: []*TestPlugin{
1607 {
1608 name: "skip1",
1609 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1610 },
1611 {
1612 name: "skip2",
1613 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1614 },
1615 {
1616 name: "skip3",
1617 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1618 },
1619 },
1620 wantPreFilterResult: nil,
1621 wantSkippedPlugins: sets.New("skip1", "skip2", "skip3"),
1622 wantStatusCode: framework.Success,
1623 },
1624 {
1625 name: "some PreFilter plugins returned skip",
1626 plugins: []*TestPlugin{
1627 {
1628 name: "skip1",
1629 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1630 },
1631 {
1632 name: "success1",
1633 },
1634 {
1635 name: "skip2",
1636 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1637 },
1638 {
1639 name: "success2",
1640 },
1641 },
1642 wantPreFilterResult: nil,
1643 wantSkippedPlugins: sets.New("skip1", "skip2"),
1644 wantStatusCode: framework.Success,
1645 },
1646 {
1647 name: "one PreFilter plugin returned Unschedulable, but another PreFilter plugin should be executed",
1648 plugins: []*TestPlugin{
1649 {
1650 name: "unschedulable",
1651 inj: injectedResult{PreFilterStatus: int(framework.Unschedulable)},
1652 },
1653 {
1654
1655 name: "skip",
1656 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1657 },
1658 },
1659 wantPreFilterResult: nil,
1660 wantSkippedPlugins: sets.New("skip"),
1661 wantStatusCode: framework.Unschedulable,
1662 },
1663 {
1664 name: "one PreFilter plugin returned UnschedulableAndUnresolvable, and all other plugins aren't executed",
1665 plugins: []*TestPlugin{
1666 {
1667 name: "unresolvable",
1668 inj: injectedResult{PreFilterStatus: int(framework.UnschedulableAndUnresolvable)},
1669 },
1670 {
1671
1672 name: "skip",
1673 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1674 },
1675 },
1676 wantPreFilterResult: nil,
1677 wantStatusCode: framework.UnschedulableAndUnresolvable,
1678 },
1679 {
1680 name: "all nodes are filtered out by prefilter result, but other plugins aren't executed because we consider all nodes are filtered out by UnschedulableAndUnresolvable",
1681 plugins: []*TestPlugin{
1682 {
1683 name: "reject-all-nodes",
1684 inj: injectedResult{PreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()}},
1685 },
1686 {
1687
1688 name: "skip",
1689 inj: injectedResult{PreFilterStatus: int(framework.Skip)},
1690 },
1691 },
1692 wantPreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()},
1693 wantSkippedPlugins: sets.New[string](),
1694 wantStatusCode: framework.UnschedulableAndUnresolvable,
1695 },
1696 }
1697 for _, tt := range tests {
1698 t.Run(tt.name, func(t *testing.T) {
1699 r := make(Registry)
1700 enabled := make([]config.Plugin, len(tt.plugins))
1701 for i, p := range tt.plugins {
1702 p := p
1703 enabled[i].Name = p.name
1704 if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1705 return p, nil
1706 }); err != nil {
1707 t.Fatalf("fail to register PreFilter plugin (%s)", p.Name())
1708 }
1709 }
1710
1711 ctx, cancel := context.WithCancel(context.Background())
1712 defer cancel()
1713
1714 f, err := newFrameworkWithQueueSortAndBind(
1715 ctx,
1716 r,
1717 config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
1718 )
1719 if err != nil {
1720 t.Fatalf("Failed to create framework for testing: %v", err)
1721 }
1722
1723 state := framework.NewCycleState()
1724 result, status := f.RunPreFilterPlugins(ctx, state, nil)
1725 if d := cmp.Diff(result, tt.wantPreFilterResult); d != "" {
1726 t.Errorf("wrong status. got: %v, want: %v, diff: %s", result, tt.wantPreFilterResult, d)
1727 }
1728 if status.Code() != tt.wantStatusCode {
1729 t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
1730 }
1731 skipped := state.SkipFilterPlugins
1732 if d := cmp.Diff(skipped, tt.wantSkippedPlugins); d != "" {
1733 t.Errorf("wrong skip filter plugins. got: %v, want: %v, diff: %s", skipped, tt.wantSkippedPlugins, d)
1734 }
1735 })
1736 }
1737 }
1738
1739 func TestRunPreFilterExtensionRemovePod(t *testing.T) {
1740 tests := []struct {
1741 name string
1742 plugins []*TestPlugin
1743 skippedPluginNames sets.Set[string]
1744 wantStatusCode framework.Code
1745 }{
1746 {
1747 name: "no plugins are skipped and all RemovePod() returned success",
1748 plugins: []*TestPlugin{
1749 {
1750 name: "success1",
1751 },
1752 {
1753 name: "success2",
1754 },
1755 },
1756 wantStatusCode: framework.Success,
1757 },
1758 {
1759 name: "one RemovePod() returned error",
1760 plugins: []*TestPlugin{
1761 {
1762 name: "success1",
1763 },
1764 {
1765 name: "error1",
1766 inj: injectedResult{PreFilterRemovePodStatus: int(framework.Error)},
1767 },
1768 },
1769 wantStatusCode: framework.Error,
1770 },
1771 {
1772 name: "one RemovePod() is skipped",
1773 plugins: []*TestPlugin{
1774 {
1775 name: "success1",
1776 },
1777 {
1778 name: "skipped",
1779
1780 inj: injectedResult{PreFilterRemovePodStatus: int(framework.Error)},
1781 },
1782 },
1783 skippedPluginNames: sets.New("skipped"),
1784 wantStatusCode: framework.Success,
1785 },
1786 }
1787 for _, tt := range tests {
1788 t.Run(tt.name, func(t *testing.T) {
1789 r := make(Registry)
1790 enabled := make([]config.Plugin, len(tt.plugins))
1791 for i, p := range tt.plugins {
1792 p := p
1793 enabled[i].Name = p.name
1794 if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1795 return p, nil
1796 }); err != nil {
1797 t.Fatalf("fail to register PreFilterExtension plugin (%s)", p.Name())
1798 }
1799 }
1800
1801 ctx, cancel := context.WithCancel(context.Background())
1802 defer cancel()
1803
1804 f, err := newFrameworkWithQueueSortAndBind(
1805 ctx,
1806 r,
1807 config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
1808 )
1809 if err != nil {
1810 t.Fatalf("Failed to create framework for testing: %v", err)
1811 }
1812
1813 state := framework.NewCycleState()
1814 state.SkipFilterPlugins = tt.skippedPluginNames
1815 status := f.RunPreFilterExtensionRemovePod(ctx, state, nil, nil, nil)
1816 if status.Code() != tt.wantStatusCode {
1817 t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
1818 }
1819 })
1820 }
1821 }
1822
1823 func TestRunPreFilterExtensionAddPod(t *testing.T) {
1824 tests := []struct {
1825 name string
1826 plugins []*TestPlugin
1827 skippedPluginNames sets.Set[string]
1828 wantStatusCode framework.Code
1829 }{
1830 {
1831 name: "no plugins are skipped and all AddPod() returned success",
1832 plugins: []*TestPlugin{
1833 {
1834 name: "success1",
1835 },
1836 {
1837 name: "success2",
1838 },
1839 },
1840 wantStatusCode: framework.Success,
1841 },
1842 {
1843 name: "one AddPod() returned error",
1844 plugins: []*TestPlugin{
1845 {
1846 name: "success1",
1847 },
1848 {
1849 name: "error1",
1850 inj: injectedResult{PreFilterAddPodStatus: int(framework.Error)},
1851 },
1852 },
1853 wantStatusCode: framework.Error,
1854 },
1855 {
1856 name: "one AddPod() is skipped",
1857 plugins: []*TestPlugin{
1858 {
1859 name: "success1",
1860 },
1861 {
1862 name: "skipped",
1863
1864 inj: injectedResult{PreFilterAddPodStatus: int(framework.Error)},
1865 },
1866 },
1867 skippedPluginNames: sets.New("skipped"),
1868 wantStatusCode: framework.Success,
1869 },
1870 }
1871 for _, tt := range tests {
1872 t.Run(tt.name, func(t *testing.T) {
1873 r := make(Registry)
1874 enabled := make([]config.Plugin, len(tt.plugins))
1875 for i, p := range tt.plugins {
1876 p := p
1877 enabled[i].Name = p.name
1878 if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
1879 return p, nil
1880 }); err != nil {
1881 t.Fatalf("fail to register PreFilterExtension plugin (%s)", p.Name())
1882 }
1883 }
1884
1885 ctx, cancel := context.WithCancel(context.Background())
1886 defer cancel()
1887
1888 f, err := newFrameworkWithQueueSortAndBind(
1889 ctx,
1890 r,
1891 config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
1892 )
1893 if err != nil {
1894 t.Fatalf("Failed to create framework for testing: %v", err)
1895 }
1896
1897 state := framework.NewCycleState()
1898 state.SkipFilterPlugins = tt.skippedPluginNames
1899 status := f.RunPreFilterExtensionAddPod(ctx, state, nil, nil, nil)
1900 if status.Code() != tt.wantStatusCode {
1901 t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
1902 }
1903 })
1904 }
1905 }
1906
1907 func TestFilterPlugins(t *testing.T) {
1908 tests := []struct {
1909 name string
1910 plugins []*TestPlugin
1911 skippedPlugins sets.Set[string]
1912 wantStatus *framework.Status
1913 }{
1914 {
1915 name: "SuccessFilter",
1916 plugins: []*TestPlugin{
1917 {
1918 name: "TestPlugin",
1919 inj: injectedResult{FilterStatus: int(framework.Success)},
1920 },
1921 },
1922 wantStatus: nil,
1923 },
1924 {
1925 name: "ErrorFilter",
1926 plugins: []*TestPlugin{
1927 {
1928 name: "TestPlugin",
1929 inj: injectedResult{FilterStatus: int(framework.Error)},
1930 },
1931 },
1932 wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin"),
1933 },
1934 {
1935 name: "UnschedulableFilter",
1936 plugins: []*TestPlugin{
1937 {
1938 name: "TestPlugin",
1939 inj: injectedResult{FilterStatus: int(framework.Unschedulable)},
1940 },
1941 },
1942 wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin"),
1943 },
1944 {
1945 name: "UnschedulableAndUnresolvableFilter",
1946 plugins: []*TestPlugin{
1947 {
1948 name: "TestPlugin",
1949 inj: injectedResult{
1950 FilterStatus: int(framework.UnschedulableAndUnresolvable)},
1951 },
1952 },
1953 wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectFilterReason).WithPlugin("TestPlugin"),
1954 },
1955
1956 {
1957 name: "ErrorAndErrorFilters",
1958 plugins: []*TestPlugin{
1959 {
1960 name: "TestPlugin1",
1961 inj: injectedResult{FilterStatus: int(framework.Error)},
1962 },
1963 {
1964 name: "TestPlugin2",
1965 inj: injectedResult{FilterStatus: int(framework.Error)},
1966 },
1967 },
1968 wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin1" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin1"),
1969 },
1970 {
1971 name: "UnschedulableAndUnschedulableFilters",
1972 plugins: []*TestPlugin{
1973 {
1974 name: "TestPlugin1",
1975 inj: injectedResult{FilterStatus: int(framework.Unschedulable)},
1976 },
1977 {
1978 name: "TestPlugin2",
1979 inj: injectedResult{FilterStatus: int(framework.Unschedulable)},
1980 },
1981 },
1982 wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin1"),
1983 },
1984 {
1985 name: "UnschedulableAndUnschedulableAndUnresolvableFilters",
1986 plugins: []*TestPlugin{
1987 {
1988 name: "TestPlugin1",
1989 inj: injectedResult{FilterStatus: int(framework.UnschedulableAndUnresolvable)},
1990 },
1991 {
1992 name: "TestPlugin2",
1993 inj: injectedResult{FilterStatus: int(framework.Unschedulable)},
1994 },
1995 },
1996 wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectFilterReason).WithPlugin("TestPlugin1"),
1997 },
1998 {
1999 name: "SuccessAndSuccessFilters",
2000 plugins: []*TestPlugin{
2001 {
2002 name: "TestPlugin1",
2003 inj: injectedResult{FilterStatus: int(framework.Success)},
2004 },
2005 {
2006 name: "TestPlugin2",
2007 inj: injectedResult{FilterStatus: int(framework.Success)},
2008 },
2009 },
2010 wantStatus: nil,
2011 },
2012 {
2013 name: "SuccessAndSkipFilters",
2014 plugins: []*TestPlugin{
2015 {
2016 name: "TestPlugin1",
2017 inj: injectedResult{FilterStatus: int(framework.Success)},
2018 },
2019
2020 {
2021 name: "TestPlugin2",
2022 inj: injectedResult{FilterStatus: int(framework.Error)},
2023 },
2024 },
2025 wantStatus: nil,
2026 skippedPlugins: sets.New("TestPlugin2"),
2027 },
2028 {
2029 name: "ErrorAndSuccessFilters",
2030 plugins: []*TestPlugin{
2031 {
2032 name: "TestPlugin1",
2033 inj: injectedResult{FilterStatus: int(framework.Error)},
2034 },
2035 {
2036 name: "TestPlugin2",
2037 inj: injectedResult{FilterStatus: int(framework.Success)},
2038 },
2039 },
2040 wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin1" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin1"),
2041 },
2042 {
2043 name: "SuccessAndErrorFilters",
2044 plugins: []*TestPlugin{
2045 {
2046
2047 name: "TestPlugin1",
2048 inj: injectedResult{FilterStatus: int(framework.Success)},
2049 },
2050 {
2051 name: "TestPlugin2",
2052 inj: injectedResult{FilterStatus: int(framework.Error)},
2053 },
2054 },
2055 wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin2" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin2"),
2056 },
2057 {
2058 name: "SuccessAndUnschedulableFilters",
2059 plugins: []*TestPlugin{
2060 {
2061 name: "TestPlugin1",
2062 inj: injectedResult{FilterStatus: int(framework.Success)},
2063 },
2064 {
2065 name: "TestPlugin2",
2066 inj: injectedResult{FilterStatus: int(framework.Unschedulable)},
2067 },
2068 },
2069 wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin2"),
2070 },
2071 }
2072
2073 for _, tt := range tests {
2074 t.Run(tt.name, func(t *testing.T) {
2075 registry := Registry{}
2076 cfgPls := &config.Plugins{}
2077 for _, pl := range tt.plugins {
2078
2079 tmpPl := pl
2080 if err := registry.Register(pl.name,
2081 func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2082 return tmpPl, nil
2083 }); err != nil {
2084 t.Fatalf("fail to register filter plugin (%s)", pl.name)
2085 }
2086
2087 cfgPls.Filter.Enabled = append(
2088 cfgPls.Filter.Enabled,
2089 config.Plugin{Name: pl.name})
2090 }
2091 profile := config.KubeSchedulerProfile{Plugins: cfgPls}
2092 ctx, cancel := context.WithCancel(context.Background())
2093 defer cancel()
2094
2095 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
2096 if err != nil {
2097 t.Fatalf("fail to create framework: %s", err)
2098 }
2099 state := framework.NewCycleState()
2100 state.SkipFilterPlugins = tt.skippedPlugins
2101 gotStatus := f.RunFilterPlugins(ctx, state, pod, nil)
2102 if diff := cmp.Diff(gotStatus, tt.wantStatus, cmpOpts...); diff != "" {
2103 t.Errorf("Unexpected status: (-got, +want):\n%s", diff)
2104 }
2105 })
2106 }
2107 }
2108
2109 func TestPostFilterPlugins(t *testing.T) {
2110 tests := []struct {
2111 name string
2112 plugins []*TestPlugin
2113 wantStatus *framework.Status
2114 }{
2115 {
2116 name: "a single plugin makes a Pod schedulable",
2117 plugins: []*TestPlugin{
2118 {
2119 name: "TestPlugin",
2120 inj: injectedResult{PostFilterStatus: int(framework.Success)},
2121 },
2122 },
2123 wantStatus: framework.NewStatus(framework.Success, injectReason),
2124 },
2125 {
2126 name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod schedulable",
2127 plugins: []*TestPlugin{
2128 {
2129 name: "TestPlugin1",
2130 inj: injectedResult{PostFilterStatus: int(framework.Unschedulable)},
2131 },
2132 {
2133 name: "TestPlugin2",
2134 inj: injectedResult{PostFilterStatus: int(framework.Success)},
2135 },
2136 },
2137 wantStatus: framework.NewStatus(framework.Success, injectReason),
2138 },
2139 {
2140 name: "plugin1 makes a Pod schedulable, followed by plugin2 which cannot make the Pod schedulable",
2141 plugins: []*TestPlugin{
2142 {
2143 name: "TestPlugin1",
2144 inj: injectedResult{PostFilterStatus: int(framework.Success)},
2145 },
2146 {
2147 name: "TestPlugin2",
2148 inj: injectedResult{PostFilterStatus: int(framework.Unschedulable)},
2149 },
2150 },
2151 wantStatus: framework.NewStatus(framework.Success, injectReason),
2152 },
2153 {
2154 name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod schedulable",
2155 plugins: []*TestPlugin{
2156 {
2157 name: "TestPlugin1",
2158 inj: injectedResult{PostFilterStatus: int(framework.Error)},
2159 },
2160 {
2161 name: "TestPlugin2",
2162 inj: injectedResult{PostFilterStatus: int(framework.Success)},
2163 },
2164 },
2165 wantStatus: framework.AsStatus(fmt.Errorf(injectReason)).WithPlugin("TestPlugin1"),
2166 },
2167 {
2168 name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod unresolvable",
2169 plugins: []*TestPlugin{
2170 {
2171 name: "TestPlugin1",
2172 inj: injectedResult{PostFilterStatus: int(framework.Unschedulable)},
2173 },
2174 {
2175 name: "TestPlugin2",
2176 inj: injectedResult{PostFilterStatus: int(framework.UnschedulableAndUnresolvable)},
2177 },
2178 },
2179 wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin2"),
2180 },
2181 {
2182 name: "both plugins failed to make a Pod schedulable",
2183 plugins: []*TestPlugin{
2184 {
2185 name: "TestPlugin1",
2186 inj: injectedResult{PostFilterStatus: int(framework.Unschedulable)},
2187 },
2188 {
2189 name: "TestPlugin2",
2190 inj: injectedResult{PostFilterStatus: int(framework.Unschedulable)},
2191 },
2192 },
2193 wantStatus: framework.NewStatus(framework.Unschedulable, []string{injectReason, injectReason}...).WithPlugin("TestPlugin1"),
2194 },
2195 }
2196
2197 for _, tt := range tests {
2198 t.Run(tt.name, func(t *testing.T) {
2199 registry := Registry{}
2200 cfgPls := &config.Plugins{}
2201 for _, pl := range tt.plugins {
2202
2203 tmpPl := pl
2204 if err := registry.Register(pl.name,
2205 func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2206 return tmpPl, nil
2207 }); err != nil {
2208 t.Fatalf("fail to register postFilter plugin (%s)", pl.name)
2209 }
2210
2211 cfgPls.PostFilter.Enabled = append(
2212 cfgPls.PostFilter.Enabled,
2213 config.Plugin{Name: pl.name},
2214 )
2215 }
2216 profile := config.KubeSchedulerProfile{Plugins: cfgPls}
2217 ctx, cancel := context.WithCancel(context.Background())
2218 defer cancel()
2219 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
2220 if err != nil {
2221 t.Fatalf("fail to create framework: %s", err)
2222 }
2223 _, gotStatus := f.RunPostFilterPlugins(ctx, nil, pod, nil)
2224 if !reflect.DeepEqual(gotStatus, tt.wantStatus) {
2225 t.Errorf("Unexpected status. got: %v, want: %v", gotStatus, tt.wantStatus)
2226 }
2227 })
2228 }
2229 }
2230
2231 func TestFilterPluginsWithNominatedPods(t *testing.T) {
2232 tests := []struct {
2233 name string
2234 preFilterPlugin *TestPlugin
2235 filterPlugin *TestPlugin
2236 pod *v1.Pod
2237 nominatedPod *v1.Pod
2238 node *v1.Node
2239 nodeInfo *framework.NodeInfo
2240 wantStatus *framework.Status
2241 }{
2242 {
2243 name: "node has no nominated pod",
2244 preFilterPlugin: nil,
2245 filterPlugin: nil,
2246 pod: lowPriorityPod,
2247 nominatedPod: nil,
2248 node: node,
2249 nodeInfo: framework.NewNodeInfo(pod),
2250 wantStatus: nil,
2251 },
2252 {
2253 name: "node has a high-priority nominated pod and all filters succeed",
2254 preFilterPlugin: &TestPlugin{
2255 name: "TestPlugin1",
2256 inj: injectedResult{
2257 PreFilterAddPodStatus: int(framework.Success),
2258 },
2259 },
2260 filterPlugin: &TestPlugin{
2261 name: "TestPlugin2",
2262 inj: injectedResult{
2263 FilterStatus: int(framework.Success),
2264 },
2265 },
2266 pod: lowPriorityPod,
2267 nominatedPod: highPriorityPod,
2268 node: node,
2269 nodeInfo: framework.NewNodeInfo(pod),
2270 wantStatus: nil,
2271 },
2272 {
2273 name: "node has a high-priority nominated pod and pre filters fail",
2274 preFilterPlugin: &TestPlugin{
2275 name: "TestPlugin1",
2276 inj: injectedResult{
2277 PreFilterAddPodStatus: int(framework.Error),
2278 },
2279 },
2280 filterPlugin: nil,
2281 pod: lowPriorityPod,
2282 nominatedPod: highPriorityPod,
2283 node: node,
2284 nodeInfo: framework.NewNodeInfo(pod),
2285 wantStatus: framework.AsStatus(fmt.Errorf(`running AddPod on PreFilter plugin "TestPlugin1": %w`, errInjectedStatus)),
2286 },
2287 {
2288 name: "node has a high-priority nominated pod and filters fail",
2289 preFilterPlugin: &TestPlugin{
2290 name: "TestPlugin1",
2291 inj: injectedResult{
2292 PreFilterAddPodStatus: int(framework.Success),
2293 },
2294 },
2295 filterPlugin: &TestPlugin{
2296 name: "TestPlugin2",
2297 inj: injectedResult{
2298 FilterStatus: int(framework.Error),
2299 },
2300 },
2301 pod: lowPriorityPod,
2302 nominatedPod: highPriorityPod,
2303 node: node,
2304 nodeInfo: framework.NewNodeInfo(pod),
2305 wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin2" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin2"),
2306 },
2307 {
2308 name: "node has a low-priority nominated pod and pre filters return unschedulable",
2309 preFilterPlugin: &TestPlugin{
2310 name: "TestPlugin1",
2311 inj: injectedResult{
2312 PreFilterAddPodStatus: int(framework.Unschedulable),
2313 },
2314 },
2315 filterPlugin: &TestPlugin{
2316 name: "TestPlugin2",
2317 inj: injectedResult{
2318 FilterStatus: int(framework.Success),
2319 },
2320 },
2321 pod: highPriorityPod,
2322 nominatedPod: lowPriorityPod,
2323 node: node,
2324 nodeInfo: framework.NewNodeInfo(pod),
2325 wantStatus: nil,
2326 },
2327 }
2328
2329 for _, tt := range tests {
2330 t.Run(tt.name, func(t *testing.T) {
2331 logger, _ := ktesting.NewTestContext(t)
2332 registry := Registry{}
2333 cfgPls := &config.Plugins{}
2334
2335 if tt.preFilterPlugin != nil {
2336 if err := registry.Register(tt.preFilterPlugin.name,
2337 func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2338 return tt.preFilterPlugin, nil
2339 }); err != nil {
2340 t.Fatalf("fail to register preFilter plugin (%s)", tt.preFilterPlugin.name)
2341 }
2342 cfgPls.PreFilter.Enabled = append(
2343 cfgPls.PreFilter.Enabled,
2344 config.Plugin{Name: tt.preFilterPlugin.name},
2345 )
2346 }
2347 if tt.filterPlugin != nil {
2348 if err := registry.Register(tt.filterPlugin.name,
2349 func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2350 return tt.filterPlugin, nil
2351 }); err != nil {
2352 t.Fatalf("fail to register filter plugin (%s)", tt.filterPlugin.name)
2353 }
2354 cfgPls.Filter.Enabled = append(
2355 cfgPls.Filter.Enabled,
2356 config.Plugin{Name: tt.filterPlugin.name},
2357 )
2358 }
2359
2360 podNominator := internalqueue.NewPodNominator(nil)
2361 if tt.nominatedPod != nil {
2362 podNominator.AddNominatedPod(
2363 logger,
2364 mustNewPodInfo(t, tt.nominatedPod),
2365 &framework.NominatingInfo{NominatingMode: framework.ModeOverride, NominatedNodeName: nodeName})
2366 }
2367 profile := config.KubeSchedulerProfile{Plugins: cfgPls}
2368 ctx, cancel := context.WithCancel(context.Background())
2369 defer cancel()
2370 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile, WithPodNominator(podNominator))
2371 if err != nil {
2372 t.Fatalf("fail to create framework: %s", err)
2373 }
2374 tt.nodeInfo.SetNode(tt.node)
2375 gotStatus := f.RunFilterPluginsWithNominatedPods(ctx, framework.NewCycleState(), tt.pod, tt.nodeInfo)
2376 if diff := cmp.Diff(gotStatus, tt.wantStatus, cmpOpts...); diff != "" {
2377 t.Errorf("Unexpected status: (-got, +want):\n%s", diff)
2378 }
2379 })
2380 }
2381 }
2382
2383 func TestPreBindPlugins(t *testing.T) {
2384 tests := []struct {
2385 name string
2386 plugins []*TestPlugin
2387 wantStatus *framework.Status
2388 }{
2389 {
2390 name: "NoPreBindPlugin",
2391 plugins: []*TestPlugin{},
2392 wantStatus: nil,
2393 },
2394 {
2395 name: "SuccessPreBindPlugins",
2396 plugins: []*TestPlugin{
2397 {
2398 name: "TestPlugin",
2399 inj: injectedResult{PreBindStatus: int(framework.Success)},
2400 },
2401 },
2402 wantStatus: nil,
2403 },
2404 {
2405 name: "UnschedulablePreBindPlugin",
2406 plugins: []*TestPlugin{
2407 {
2408 name: "TestPlugin",
2409 inj: injectedResult{PreBindStatus: int(framework.Unschedulable)},
2410 },
2411 },
2412 wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
2413 },
2414 {
2415 name: "ErrorPreBindPlugin",
2416 plugins: []*TestPlugin{
2417 {
2418 name: "TestPlugin",
2419 inj: injectedResult{PreBindStatus: int(framework.Error)},
2420 },
2421 },
2422 wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
2423 },
2424 {
2425 name: "UnschedulablePreBindPlugin",
2426 plugins: []*TestPlugin{
2427 {
2428 name: "TestPlugin",
2429 inj: injectedResult{PreBindStatus: int(framework.UnschedulableAndUnresolvable)},
2430 },
2431 },
2432 wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
2433 },
2434 {
2435 name: "SuccessErrorPreBindPlugins",
2436 plugins: []*TestPlugin{
2437 {
2438 name: "TestPlugin",
2439 inj: injectedResult{PreBindStatus: int(framework.Success)},
2440 },
2441 {
2442 name: "TestPlugin 1",
2443 inj: injectedResult{PreBindStatus: int(framework.Error)},
2444 },
2445 },
2446 wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin 1": %w`, errInjectedStatus)),
2447 },
2448 {
2449 name: "ErrorSuccessPreBindPlugin",
2450 plugins: []*TestPlugin{
2451 {
2452 name: "TestPlugin",
2453 inj: injectedResult{PreBindStatus: int(framework.Error)},
2454 },
2455 {
2456 name: "TestPlugin 1",
2457 inj: injectedResult{PreBindStatus: int(framework.Success)},
2458 },
2459 },
2460 wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
2461 },
2462 {
2463 name: "SuccessSuccessPreBindPlugin",
2464 plugins: []*TestPlugin{
2465 {
2466 name: "TestPlugin",
2467 inj: injectedResult{PreBindStatus: int(framework.Success)},
2468 },
2469 {
2470 name: "TestPlugin 1",
2471 inj: injectedResult{PreBindStatus: int(framework.Success)},
2472 },
2473 },
2474 wantStatus: nil,
2475 },
2476 {
2477 name: "ErrorAndErrorPlugins",
2478 plugins: []*TestPlugin{
2479 {
2480 name: "TestPlugin",
2481 inj: injectedResult{PreBindStatus: int(framework.Error)},
2482 },
2483 {
2484 name: "TestPlugin 1",
2485 inj: injectedResult{PreBindStatus: int(framework.Error)},
2486 },
2487 },
2488 wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
2489 },
2490 {
2491 name: "UnschedulableAndSuccessPreBindPlugin",
2492 plugins: []*TestPlugin{
2493 {
2494 name: "TestPlugin",
2495 inj: injectedResult{PreBindStatus: int(framework.Unschedulable)},
2496 },
2497 {
2498 name: "TestPlugin 1",
2499 inj: injectedResult{PreBindStatus: int(framework.Success)},
2500 },
2501 },
2502 wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
2503 },
2504 }
2505
2506 for _, tt := range tests {
2507 t.Run(tt.name, func(t *testing.T) {
2508 registry := Registry{}
2509 configPlugins := &config.Plugins{}
2510
2511 for _, pl := range tt.plugins {
2512 tmpPl := pl
2513 if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2514 return tmpPl, nil
2515 }); err != nil {
2516 t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
2517 }
2518
2519 configPlugins.PreBind.Enabled = append(
2520 configPlugins.PreBind.Enabled,
2521 config.Plugin{Name: pl.name},
2522 )
2523 }
2524 profile := config.KubeSchedulerProfile{Plugins: configPlugins}
2525 ctx, cancel := context.WithCancel(context.Background())
2526 defer cancel()
2527 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
2528 if err != nil {
2529 t.Fatalf("fail to create framework: %s", err)
2530 }
2531
2532 status := f.RunPreBindPlugins(ctx, nil, pod, "")
2533
2534 if !reflect.DeepEqual(status, tt.wantStatus) {
2535 t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
2536 }
2537 })
2538 }
2539 }
2540
2541 func TestReservePlugins(t *testing.T) {
2542 tests := []struct {
2543 name string
2544 plugins []*TestPlugin
2545 wantStatus *framework.Status
2546 }{
2547 {
2548 name: "NoReservePlugin",
2549 plugins: []*TestPlugin{},
2550 wantStatus: nil,
2551 },
2552 {
2553 name: "SuccessReservePlugins",
2554 plugins: []*TestPlugin{
2555 {
2556 name: "TestPlugin",
2557 inj: injectedResult{ReserveStatus: int(framework.Success)},
2558 },
2559 },
2560 wantStatus: nil,
2561 },
2562 {
2563 name: "UnschedulableReservePlugin",
2564 plugins: []*TestPlugin{
2565 {
2566 name: "TestPlugin",
2567 inj: injectedResult{ReserveStatus: int(framework.Unschedulable)},
2568 },
2569 },
2570 wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
2571 },
2572 {
2573 name: "ErrorReservePlugin",
2574 plugins: []*TestPlugin{
2575 {
2576 name: "TestPlugin",
2577 inj: injectedResult{ReserveStatus: int(framework.Error)},
2578 },
2579 },
2580 wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
2581 },
2582 {
2583 name: "UnschedulableReservePlugin",
2584 plugins: []*TestPlugin{
2585 {
2586 name: "TestPlugin",
2587 inj: injectedResult{ReserveStatus: int(framework.UnschedulableAndUnresolvable)},
2588 },
2589 },
2590 wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
2591 },
2592 {
2593 name: "SuccessSuccessReservePlugins",
2594 plugins: []*TestPlugin{
2595 {
2596 name: "TestPlugin",
2597 inj: injectedResult{ReserveStatus: int(framework.Success)},
2598 },
2599 {
2600 name: "TestPlugin 1",
2601 inj: injectedResult{ReserveStatus: int(framework.Success)},
2602 },
2603 },
2604 wantStatus: nil,
2605 },
2606 {
2607 name: "ErrorErrorReservePlugins",
2608 plugins: []*TestPlugin{
2609 {
2610 name: "TestPlugin",
2611 inj: injectedResult{ReserveStatus: int(framework.Error)},
2612 },
2613 {
2614 name: "TestPlugin 1",
2615 inj: injectedResult{ReserveStatus: int(framework.Error)},
2616 },
2617 },
2618 wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
2619 },
2620 {
2621 name: "SuccessErrorReservePlugins",
2622 plugins: []*TestPlugin{
2623 {
2624 name: "TestPlugin",
2625 inj: injectedResult{ReserveStatus: int(framework.Success)},
2626 },
2627 {
2628 name: "TestPlugin 1",
2629 inj: injectedResult{ReserveStatus: int(framework.Error)},
2630 },
2631 },
2632 wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin 1": %w`, errInjectedStatus)),
2633 },
2634 {
2635 name: "ErrorSuccessReservePlugin",
2636 plugins: []*TestPlugin{
2637 {
2638 name: "TestPlugin",
2639 inj: injectedResult{ReserveStatus: int(framework.Error)},
2640 },
2641 {
2642 name: "TestPlugin 1",
2643 inj: injectedResult{ReserveStatus: int(framework.Success)},
2644 },
2645 },
2646 wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
2647 },
2648 {
2649 name: "UnschedulableAndSuccessReservePlugin",
2650 plugins: []*TestPlugin{
2651 {
2652 name: "TestPlugin",
2653 inj: injectedResult{ReserveStatus: int(framework.Unschedulable)},
2654 },
2655 {
2656 name: "TestPlugin 1",
2657 inj: injectedResult{ReserveStatus: int(framework.Success)},
2658 },
2659 },
2660 wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
2661 },
2662 }
2663
2664 for _, tt := range tests {
2665 t.Run(tt.name, func(t *testing.T) {
2666 registry := Registry{}
2667 configPlugins := &config.Plugins{}
2668
2669 for _, pl := range tt.plugins {
2670 tmpPl := pl
2671 if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2672 return tmpPl, nil
2673 }); err != nil {
2674 t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
2675 }
2676
2677 configPlugins.Reserve.Enabled = append(
2678 configPlugins.Reserve.Enabled,
2679 config.Plugin{Name: pl.name},
2680 )
2681 }
2682 profile := config.KubeSchedulerProfile{Plugins: configPlugins}
2683 ctx, cancel := context.WithCancel(context.Background())
2684 defer cancel()
2685 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
2686 if err != nil {
2687 t.Fatalf("fail to create framework: %s", err)
2688 }
2689
2690 status := f.RunReservePluginsReserve(ctx, nil, pod, "")
2691
2692 if !reflect.DeepEqual(status, tt.wantStatus) {
2693 t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
2694 }
2695 })
2696 }
2697 }
2698
2699 func TestPermitPlugins(t *testing.T) {
2700 tests := []struct {
2701 name string
2702 plugins []*TestPlugin
2703 want *framework.Status
2704 }{
2705 {
2706 name: "NilPermitPlugin",
2707 plugins: []*TestPlugin{},
2708 want: nil,
2709 },
2710 {
2711 name: "SuccessPermitPlugin",
2712 plugins: []*TestPlugin{
2713 {
2714 name: "TestPlugin",
2715 inj: injectedResult{PermitStatus: int(framework.Success)},
2716 },
2717 },
2718 want: nil,
2719 },
2720 {
2721 name: "UnschedulablePermitPlugin",
2722 plugins: []*TestPlugin{
2723 {
2724 name: "TestPlugin",
2725 inj: injectedResult{PermitStatus: int(framework.Unschedulable)},
2726 },
2727 },
2728 want: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
2729 },
2730 {
2731 name: "ErrorPermitPlugin",
2732 plugins: []*TestPlugin{
2733 {
2734 name: "TestPlugin",
2735 inj: injectedResult{PermitStatus: int(framework.Error)},
2736 },
2737 },
2738 want: framework.AsStatus(fmt.Errorf(`running Permit plugin "TestPlugin": %w`, errInjectedStatus)).WithPlugin("TestPlugin"),
2739 },
2740 {
2741 name: "UnschedulableAndUnresolvablePermitPlugin",
2742 plugins: []*TestPlugin{
2743 {
2744 name: "TestPlugin",
2745 inj: injectedResult{PermitStatus: int(framework.UnschedulableAndUnresolvable)},
2746 },
2747 },
2748 want: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
2749 },
2750 {
2751 name: "WaitPermitPlugin",
2752 plugins: []*TestPlugin{
2753 {
2754 name: "TestPlugin",
2755 inj: injectedResult{PermitStatus: int(framework.Wait)},
2756 },
2757 },
2758 want: framework.NewStatus(framework.Wait, `one or more plugins asked to wait and no plugin rejected pod ""`),
2759 },
2760 {
2761 name: "SuccessSuccessPermitPlugin",
2762 plugins: []*TestPlugin{
2763 {
2764 name: "TestPlugin",
2765 inj: injectedResult{PermitStatus: int(framework.Success)},
2766 },
2767 {
2768 name: "TestPlugin 1",
2769 inj: injectedResult{PermitStatus: int(framework.Success)},
2770 },
2771 },
2772 want: nil,
2773 },
2774 {
2775 name: "ErrorAndErrorPlugins",
2776 plugins: []*TestPlugin{
2777 {
2778 name: "TestPlugin",
2779 inj: injectedResult{PermitStatus: int(framework.Error)},
2780 },
2781 {
2782 name: "TestPlugin 1",
2783 inj: injectedResult{PermitStatus: int(framework.Error)},
2784 },
2785 },
2786 want: framework.AsStatus(fmt.Errorf(`running Permit plugin "TestPlugin": %w`, errInjectedStatus)).WithPlugin("TestPlugin"),
2787 },
2788 }
2789
2790 for _, tt := range tests {
2791 t.Run(tt.name, func(t *testing.T) {
2792 registry := Registry{}
2793 configPlugins := &config.Plugins{}
2794
2795 for _, pl := range tt.plugins {
2796 tmpPl := pl
2797 if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
2798 return tmpPl, nil
2799 }); err != nil {
2800 t.Fatalf("Unable to register Permit plugin: %s", pl.name)
2801 }
2802
2803 configPlugins.Permit.Enabled = append(
2804 configPlugins.Permit.Enabled,
2805 config.Plugin{Name: pl.name},
2806 )
2807 }
2808 profile := config.KubeSchedulerProfile{Plugins: configPlugins}
2809 ctx, cancel := context.WithCancel(context.Background())
2810 defer cancel()
2811 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
2812 if err != nil {
2813 t.Fatalf("fail to create framework: %s", err)
2814 }
2815
2816 status := f.RunPermitPlugins(ctx, nil, pod, "")
2817 if !reflect.DeepEqual(status, tt.want) {
2818 t.Errorf("wrong status code. got %v, want %v", status, tt.want)
2819 }
2820 })
2821 }
2822 }
2823
2824
2825 func withMetricsRecorder(recorder *metrics.MetricAsyncRecorder) Option {
2826 return func(o *frameworkOptions) {
2827 o.metricsRecorder = recorder
2828 }
2829 }
2830
2831 func TestRecordingMetrics(t *testing.T) {
2832 state := &framework.CycleState{}
2833 state.SetRecordPluginMetrics(true)
2834
2835 tests := []struct {
2836 name string
2837 action func(f framework.Framework)
2838 inject injectedResult
2839 wantExtensionPoint string
2840 wantStatus framework.Code
2841 }{
2842 {
2843 name: "PreFilter - Success",
2844 action: func(f framework.Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
2845 wantExtensionPoint: "PreFilter",
2846 wantStatus: framework.Success,
2847 },
2848 {
2849 name: "PreScore - Success",
2850 action: func(f framework.Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
2851 wantExtensionPoint: "PreScore",
2852 wantStatus: framework.Success,
2853 },
2854 {
2855 name: "Score - Success",
2856 action: func(f framework.Framework) {
2857 f.RunScorePlugins(context.Background(), state, pod, BuildNodeInfos(nodes))
2858 },
2859 wantExtensionPoint: "Score",
2860 wantStatus: framework.Success,
2861 },
2862 {
2863 name: "Reserve - Success",
2864 action: func(f framework.Framework) { f.RunReservePluginsReserve(context.Background(), state, pod, "") },
2865 wantExtensionPoint: "Reserve",
2866 wantStatus: framework.Success,
2867 },
2868 {
2869 name: "Unreserve - Success",
2870 action: func(f framework.Framework) { f.RunReservePluginsUnreserve(context.Background(), state, pod, "") },
2871 wantExtensionPoint: "Unreserve",
2872 wantStatus: framework.Success,
2873 },
2874 {
2875 name: "PreBind - Success",
2876 action: func(f framework.Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
2877 wantExtensionPoint: "PreBind",
2878 wantStatus: framework.Success,
2879 },
2880 {
2881 name: "Bind - Success",
2882 action: func(f framework.Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
2883 wantExtensionPoint: "Bind",
2884 wantStatus: framework.Success,
2885 },
2886 {
2887 name: "PostBind - Success",
2888 action: func(f framework.Framework) { f.RunPostBindPlugins(context.Background(), state, pod, "") },
2889 wantExtensionPoint: "PostBind",
2890 wantStatus: framework.Success,
2891 },
2892 {
2893 name: "Permit - Success",
2894 action: func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
2895 wantExtensionPoint: "Permit",
2896 wantStatus: framework.Success,
2897 },
2898
2899 {
2900 name: "PreFilter - Error",
2901 action: func(f framework.Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
2902 inject: injectedResult{PreFilterStatus: int(framework.Error)},
2903 wantExtensionPoint: "PreFilter",
2904 wantStatus: framework.Error,
2905 },
2906 {
2907 name: "PreScore - Error",
2908 action: func(f framework.Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
2909 inject: injectedResult{PreScoreStatus: int(framework.Error)},
2910 wantExtensionPoint: "PreScore",
2911 wantStatus: framework.Error,
2912 },
2913 {
2914 name: "Score - Error",
2915 action: func(f framework.Framework) {
2916 f.RunScorePlugins(context.Background(), state, pod, BuildNodeInfos(nodes))
2917 },
2918 inject: injectedResult{ScoreStatus: int(framework.Error)},
2919 wantExtensionPoint: "Score",
2920 wantStatus: framework.Error,
2921 },
2922 {
2923 name: "Reserve - Error",
2924 action: func(f framework.Framework) { f.RunReservePluginsReserve(context.Background(), state, pod, "") },
2925 inject: injectedResult{ReserveStatus: int(framework.Error)},
2926 wantExtensionPoint: "Reserve",
2927 wantStatus: framework.Error,
2928 },
2929 {
2930 name: "PreBind - Error",
2931 action: func(f framework.Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
2932 inject: injectedResult{PreBindStatus: int(framework.Error)},
2933 wantExtensionPoint: "PreBind",
2934 wantStatus: framework.Error,
2935 },
2936 {
2937 name: "Bind - Error",
2938 action: func(f framework.Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
2939 inject: injectedResult{BindStatus: int(framework.Error)},
2940 wantExtensionPoint: "Bind",
2941 wantStatus: framework.Error,
2942 },
2943 {
2944 name: "Permit - Error",
2945 action: func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
2946 inject: injectedResult{PermitStatus: int(framework.Error)},
2947 wantExtensionPoint: "Permit",
2948 wantStatus: framework.Error,
2949 },
2950 {
2951 name: "Permit - Wait",
2952 action: func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
2953 inject: injectedResult{PermitStatus: int(framework.Wait)},
2954 wantExtensionPoint: "Permit",
2955 wantStatus: framework.Wait,
2956 },
2957 }
2958
2959 for _, tt := range tests {
2960 t.Run(tt.name, func(t *testing.T) {
2961 metrics.Register()
2962 metrics.FrameworkExtensionPointDuration.Reset()
2963 metrics.PluginExecutionDuration.Reset()
2964
2965 plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
2966 r := make(Registry)
2967 r.Register(testPlugin,
2968 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
2969 return plugin, nil
2970 })
2971 pluginSet := config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}}
2972 plugins := &config.Plugins{
2973 Score: pluginSet,
2974 PreFilter: pluginSet,
2975 Filter: pluginSet,
2976 PreScore: pluginSet,
2977 Reserve: pluginSet,
2978 Permit: pluginSet,
2979 PreBind: pluginSet,
2980 Bind: pluginSet,
2981 PostBind: pluginSet,
2982 }
2983
2984 _, ctx := ktesting.NewTestContext(t)
2985 ctx, cancel := context.WithCancel(ctx)
2986
2987 recorder := metrics.NewMetricsAsyncRecorder(100, time.Nanosecond, ctx.Done())
2988 profile := config.KubeSchedulerProfile{
2989 PercentageOfNodesToScore: ptr.To[int32](testPercentageOfNodesToScore),
2990 SchedulerName: testProfileName,
2991 Plugins: plugins,
2992 }
2993 f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile, withMetricsRecorder(recorder))
2994 if err != nil {
2995 cancel()
2996 t.Fatalf("Failed to create framework for testing: %v", err)
2997 }
2998
2999 tt.action(f)
3000
3001
3002 cancel()
3003 <-recorder.IsStoppedCh
3004
3005 recorder.FlushMetrics()
3006
3007 collectAndCompareFrameworkMetrics(t, tt.wantExtensionPoint, tt.wantStatus)
3008 collectAndComparePluginMetrics(t, tt.wantExtensionPoint, testPlugin, tt.wantStatus)
3009 })
3010 }
3011 }
3012
3013 func TestRunBindPlugins(t *testing.T) {
3014 tests := []struct {
3015 name string
3016 injects []framework.Code
3017 wantStatus framework.Code
3018 }{
3019 {
3020 name: "simple success",
3021 injects: []framework.Code{framework.Success},
3022 wantStatus: framework.Success,
3023 },
3024 {
3025 name: "error on second",
3026 injects: []framework.Code{framework.Skip, framework.Error, framework.Success},
3027 wantStatus: framework.Error,
3028 },
3029 {
3030 name: "all skip",
3031 injects: []framework.Code{framework.Skip, framework.Skip, framework.Skip},
3032 wantStatus: framework.Skip,
3033 },
3034 {
3035 name: "error on third, but not reached",
3036 injects: []framework.Code{framework.Skip, framework.Success, framework.Error},
3037 wantStatus: framework.Success,
3038 },
3039 {
3040 name: "no bind plugin, returns default binder",
3041 injects: []framework.Code{},
3042 wantStatus: framework.Success,
3043 },
3044 {
3045 name: "invalid status",
3046 injects: []framework.Code{framework.Unschedulable},
3047 wantStatus: framework.Unschedulable,
3048 },
3049 {
3050 name: "simple error",
3051 injects: []framework.Code{framework.Error},
3052 wantStatus: framework.Error,
3053 },
3054 {
3055 name: "success on second, returns success",
3056 injects: []framework.Code{framework.Skip, framework.Success},
3057 wantStatus: framework.Success,
3058 },
3059 {
3060 name: "invalid status, returns error",
3061 injects: []framework.Code{framework.Skip, framework.UnschedulableAndUnresolvable},
3062 wantStatus: framework.UnschedulableAndUnresolvable,
3063 },
3064 {
3065 name: "error after success status, returns success",
3066 injects: []framework.Code{framework.Success, framework.Error},
3067 wantStatus: framework.Success,
3068 },
3069 {
3070 name: "success before invalid status, returns success",
3071 injects: []framework.Code{framework.Success, framework.Error},
3072 wantStatus: framework.Success,
3073 },
3074 {
3075 name: "success after error status, returns error",
3076 injects: []framework.Code{framework.Error, framework.Success},
3077 wantStatus: framework.Error,
3078 },
3079 }
3080 for _, tt := range tests {
3081 t.Run(tt.name, func(t *testing.T) {
3082 metrics.Register()
3083 metrics.FrameworkExtensionPointDuration.Reset()
3084 metrics.PluginExecutionDuration.Reset()
3085
3086 pluginSet := config.PluginSet{}
3087 r := make(Registry)
3088 for i, inj := range tt.injects {
3089 name := fmt.Sprintf("bind-%d", i)
3090 plugin := &TestPlugin{name: name, inj: injectedResult{BindStatus: int(inj)}}
3091 r.Register(name,
3092 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
3093 return plugin, nil
3094 })
3095 pluginSet.Enabled = append(pluginSet.Enabled, config.Plugin{Name: name})
3096 }
3097 plugins := &config.Plugins{Bind: pluginSet}
3098 _, ctx := ktesting.NewTestContext(t)
3099 ctx, cancel := context.WithCancel(ctx)
3100 recorder := metrics.NewMetricsAsyncRecorder(100, time.Nanosecond, ctx.Done())
3101 profile := config.KubeSchedulerProfile{
3102 SchedulerName: testProfileName,
3103 PercentageOfNodesToScore: ptr.To[int32](testPercentageOfNodesToScore),
3104 Plugins: plugins,
3105 }
3106 fwk, err := newFrameworkWithQueueSortAndBind(ctx, r, profile, withMetricsRecorder(recorder))
3107 if err != nil {
3108 cancel()
3109 t.Fatal(err)
3110 }
3111
3112 st := fwk.RunBindPlugins(context.Background(), state, pod, "")
3113 if st.Code() != tt.wantStatus {
3114 t.Errorf("got status code %s, want %s", st.Code(), tt.wantStatus)
3115 }
3116
3117
3118 cancel()
3119 <-recorder.IsStoppedCh
3120
3121 recorder.FlushMetrics()
3122 collectAndCompareFrameworkMetrics(t, "Bind", tt.wantStatus)
3123 })
3124 }
3125 }
3126
3127 func TestPermitWaitDurationMetric(t *testing.T) {
3128 tests := []struct {
3129 name string
3130 inject injectedResult
3131 wantRes string
3132 }{
3133 {
3134 name: "WaitOnPermit - No Wait",
3135 },
3136 {
3137 name: "WaitOnPermit - Wait Timeout",
3138 inject: injectedResult{PermitStatus: int(framework.Wait)},
3139 wantRes: "Unschedulable",
3140 },
3141 }
3142
3143 for _, tt := range tests {
3144 t.Run(tt.name, func(t *testing.T) {
3145 metrics.Register()
3146 metrics.PermitWaitDuration.Reset()
3147
3148 plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
3149 r := make(Registry)
3150 err := r.Register(testPlugin,
3151 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
3152 return plugin, nil
3153 })
3154 if err != nil {
3155 t.Fatal(err)
3156 }
3157 plugins := &config.Plugins{
3158 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}},
3159 }
3160 profile := config.KubeSchedulerProfile{Plugins: plugins}
3161 ctx, cancel := context.WithCancel(context.Background())
3162 defer cancel()
3163 f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
3164 if err != nil {
3165 t.Fatalf("Failed to create framework for testing: %v", err)
3166 }
3167
3168 f.RunPermitPlugins(ctx, nil, pod, "")
3169 f.WaitOnPermit(ctx, pod)
3170
3171 collectAndComparePermitWaitDuration(t, tt.wantRes)
3172 })
3173 }
3174 }
3175
3176 func TestWaitOnPermit(t *testing.T) {
3177 pod := &v1.Pod{
3178 ObjectMeta: metav1.ObjectMeta{
3179 Name: "pod",
3180 UID: types.UID("pod"),
3181 },
3182 }
3183
3184 tests := []struct {
3185 name string
3186 action func(f framework.Framework)
3187 want *framework.Status
3188 }{
3189 {
3190 name: "Reject Waiting Pod",
3191 action: func(f framework.Framework) {
3192 f.GetWaitingPod(pod.UID).Reject(permitPlugin, "reject message")
3193 },
3194 want: framework.NewStatus(framework.Unschedulable, "reject message").WithPlugin(permitPlugin),
3195 },
3196 {
3197 name: "Allow Waiting Pod",
3198 action: func(f framework.Framework) {
3199 f.GetWaitingPod(pod.UID).Allow(permitPlugin)
3200 },
3201 want: nil,
3202 },
3203 }
3204
3205 for _, tt := range tests {
3206 t.Run(tt.name, func(t *testing.T) {
3207 testPermitPlugin := &TestPermitPlugin{}
3208 r := make(Registry)
3209 r.Register(permitPlugin,
3210 func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
3211 return testPermitPlugin, nil
3212 })
3213 plugins := &config.Plugins{
3214 Permit: config.PluginSet{Enabled: []config.Plugin{{Name: permitPlugin, Weight: 1}}},
3215 }
3216 profile := config.KubeSchedulerProfile{Plugins: plugins}
3217 ctx, cancel := context.WithCancel(context.Background())
3218 defer cancel()
3219 f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
3220 if err != nil {
3221 t.Fatalf("Failed to create framework for testing: %v", err)
3222 }
3223
3224 runPermitPluginsStatus := f.RunPermitPlugins(ctx, nil, pod, "")
3225 if runPermitPluginsStatus.Code() != framework.Wait {
3226 t.Fatalf("Expected RunPermitPlugins to return status %v, but got %v",
3227 framework.Wait, runPermitPluginsStatus.Code())
3228 }
3229
3230 go tt.action(f)
3231
3232 got := f.WaitOnPermit(ctx, pod)
3233 if !reflect.DeepEqual(tt.want, got) {
3234 t.Errorf("Unexpected status: want %v, but got %v", tt.want, got)
3235 }
3236 })
3237 }
3238 }
3239
3240 func TestListPlugins(t *testing.T) {
3241 tests := []struct {
3242 name string
3243 plugins *config.Plugins
3244 want *config.Plugins
3245 }{
3246 {
3247 name: "Add empty plugin",
3248 plugins: &config.Plugins{},
3249 want: &config.Plugins{
3250 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
3251 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
3252 },
3253 },
3254 {
3255 name: "Add multiple plugins",
3256 plugins: &config.Plugins{
3257 Score: config.PluginSet{Enabled: []config.Plugin{{Name: scorePlugin1, Weight: 3}, {Name: scoreWithNormalizePlugin1}}},
3258 },
3259 want: &config.Plugins{
3260 QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
3261 Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
3262 Score: config.PluginSet{Enabled: []config.Plugin{{Name: scorePlugin1, Weight: 3}, {Name: scoreWithNormalizePlugin1, Weight: 1}}},
3263 },
3264 },
3265 }
3266
3267 for _, tt := range tests {
3268 t.Run(tt.name, func(t *testing.T) {
3269 profile := config.KubeSchedulerProfile{Plugins: tt.plugins}
3270 _, ctx := ktesting.NewTestContext(t)
3271 ctx, cancel := context.WithCancel(ctx)
3272 defer cancel()
3273 f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
3274 if err != nil {
3275 t.Fatalf("Failed to create framework for testing: %v", err)
3276 }
3277 got := f.ListPlugins()
3278 if diff := cmp.Diff(tt.want, got); diff != "" {
3279 t.Errorf("unexpected plugins (-want,+got):\n%s", diff)
3280 }
3281 })
3282 }
3283 }
3284
3285 func TestClose(t *testing.T) {
3286 tests := []struct {
3287 name string
3288 plugins *config.Plugins
3289 wantErr error
3290 }{
3291 {
3292 name: "close doesn't return error",
3293 plugins: &config.Plugins{
3294 MultiPoint: config.PluginSet{
3295 Enabled: []config.Plugin{
3296 {Name: testPlugin, Weight: 5},
3297 },
3298 },
3299 },
3300 },
3301 {
3302 name: "close returns error",
3303 plugins: &config.Plugins{
3304 MultiPoint: config.PluginSet{
3305 Enabled: []config.Plugin{
3306 {Name: testPlugin, Weight: 5},
3307 {Name: testCloseErrorPlugin},
3308 },
3309 },
3310 },
3311 wantErr: errClose,
3312 },
3313 }
3314
3315 for _, tc := range tests {
3316 t.Run(tc.name, func(t *testing.T) {
3317 _, ctx := ktesting.NewTestContext(t)
3318 ctx, cancel := context.WithCancel(ctx)
3319 defer cancel()
3320 fw, err := NewFramework(ctx, registry, &config.KubeSchedulerProfile{Plugins: tc.plugins})
3321 if err != nil {
3322 t.Fatalf("Unexpected error during calling NewFramework, got %v", err)
3323 }
3324 err = fw.Close()
3325 if !errors.Is(err, tc.wantErr) {
3326 t.Fatalf("Unexpected error from Close(), got: %v, want: %v", err, tc.wantErr)
3327 }
3328 })
3329 }
3330 }
3331
3332 func buildScoreConfigDefaultWeights(ps ...string) *config.Plugins {
3333 return buildScoreConfigWithWeights(defaultWeights, ps...)
3334 }
3335
3336 func buildScoreConfigWithWeights(weights map[string]int32, ps ...string) *config.Plugins {
3337 var plugins []config.Plugin
3338 for _, p := range ps {
3339 plugins = append(plugins, config.Plugin{Name: p, Weight: weights[p]})
3340 }
3341 return &config.Plugins{Score: config.PluginSet{Enabled: plugins}}
3342 }
3343
3344 type injectedResult struct {
3345 ScoreRes int64 `json:"scoreRes,omitempty"`
3346 NormalizeRes int64 `json:"normalizeRes,omitempty"`
3347 ScoreStatus int `json:"scoreStatus,omitempty"`
3348 NormalizeStatus int `json:"normalizeStatus,omitempty"`
3349 PreFilterResult *framework.PreFilterResult `json:"preFilterResult,omitempty"`
3350 PreFilterStatus int `json:"preFilterStatus,omitempty"`
3351 PreFilterAddPodStatus int `json:"preFilterAddPodStatus,omitempty"`
3352 PreFilterRemovePodStatus int `json:"preFilterRemovePodStatus,omitempty"`
3353 FilterStatus int `json:"filterStatus,omitempty"`
3354 PostFilterStatus int `json:"postFilterStatus,omitempty"`
3355 PreScoreStatus int `json:"preScoreStatus,omitempty"`
3356 ReserveStatus int `json:"reserveStatus,omitempty"`
3357 PreBindStatus int `json:"preBindStatus,omitempty"`
3358 BindStatus int `json:"bindStatus,omitempty"`
3359 PermitStatus int `json:"permitStatus,omitempty"`
3360 }
3361
3362 func setScoreRes(inj injectedResult) (int64, *framework.Status) {
3363 if framework.Code(inj.ScoreStatus) != framework.Success {
3364 return 0, framework.NewStatus(framework.Code(inj.ScoreStatus), "injecting failure.")
3365 }
3366 return inj.ScoreRes, nil
3367 }
3368
3369 func injectNormalizeRes(inj injectedResult, scores framework.NodeScoreList) *framework.Status {
3370 if framework.Code(inj.NormalizeStatus) != framework.Success {
3371 return framework.NewStatus(framework.Code(inj.NormalizeStatus), "injecting failure.")
3372 }
3373 for i := range scores {
3374 scores[i].Score = inj.NormalizeRes
3375 }
3376 return nil
3377 }
3378
3379 func collectAndComparePluginMetrics(t *testing.T, wantExtensionPoint, wantPlugin string, wantStatus framework.Code) {
3380 t.Helper()
3381 m := metrics.PluginExecutionDuration.WithLabelValues(wantPlugin, wantExtensionPoint, wantStatus.String())
3382
3383 count, err := testutil.GetHistogramMetricCount(m)
3384 if err != nil {
3385 t.Errorf("Failed to get %s sampleCount, err: %v", metrics.PluginExecutionDuration.Name, err)
3386 }
3387 if count == 0 {
3388 t.Error("Expect at least 1 sample")
3389 }
3390 value, err := testutil.GetHistogramMetricValue(m)
3391 if err != nil {
3392 t.Errorf("Failed to get %s value, err: %v", metrics.PluginExecutionDuration.Name, err)
3393 }
3394 checkLatency(t, value)
3395 }
3396
3397 func collectAndCompareFrameworkMetrics(t *testing.T, wantExtensionPoint string, wantStatus framework.Code) {
3398 t.Helper()
3399 m := metrics.FrameworkExtensionPointDuration.WithLabelValues(wantExtensionPoint, wantStatus.String(), testProfileName)
3400
3401 count, err := testutil.GetHistogramMetricCount(m)
3402 if err != nil {
3403 t.Errorf("Failed to get %s sampleCount, err: %v", metrics.FrameworkExtensionPointDuration.Name, err)
3404 }
3405 if count != 1 {
3406 t.Errorf("Expect 1 sample, got: %v", count)
3407 }
3408 value, err := testutil.GetHistogramMetricValue(m)
3409 if err != nil {
3410 t.Errorf("Failed to get %s value, err: %v", metrics.FrameworkExtensionPointDuration.Name, err)
3411 }
3412 checkLatency(t, value)
3413 }
3414
3415 func collectAndComparePermitWaitDuration(t *testing.T, wantRes string) {
3416 m := metrics.PermitWaitDuration.WithLabelValues(wantRes)
3417 count, err := testutil.GetHistogramMetricCount(m)
3418 if err != nil {
3419 t.Errorf("Failed to get %s sampleCount, err: %v", metrics.PermitWaitDuration.Name, err)
3420 }
3421 if wantRes == "" {
3422 if count != 0 {
3423 t.Errorf("Expect 0 sample, got: %v", count)
3424 }
3425 } else {
3426 if count != 1 {
3427 t.Errorf("Expect 1 sample, got: %v", count)
3428 }
3429 value, err := testutil.GetHistogramMetricValue(m)
3430 if err != nil {
3431 t.Errorf("Failed to get %s value, err: %v", metrics.PermitWaitDuration.Name, err)
3432 }
3433 checkLatency(t, value)
3434 }
3435 }
3436
3437 func mustNewPodInfo(t *testing.T, pod *v1.Pod) *framework.PodInfo {
3438 podInfo, err := framework.NewPodInfo(pod)
3439 if err != nil {
3440 t.Fatal(err)
3441 }
3442 return podInfo
3443 }
3444
3445
3446 func BuildNodeInfos(nodes []*v1.Node) []*framework.NodeInfo {
3447 res := make([]*framework.NodeInfo, len(nodes))
3448 for i := 0; i < len(nodes); i++ {
3449 res[i] = framework.NewNodeInfo()
3450 res[i].SetNode(nodes[i])
3451 }
3452 return res
3453 }
3454
View as plain text