@@ -44,6 +44,8 @@ import (
4444 "k8s.io/kubernetes/pkg/scheduler"
4545 configtesting "k8s.io/kubernetes/pkg/scheduler/apis/config/testing"
4646 "k8s.io/kubernetes/pkg/scheduler/framework"
47+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
48+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
4749 frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
4850 st "k8s.io/kubernetes/pkg/scheduler/testing"
4951 testfwk "k8s.io/kubernetes/test/integration/framework"
@@ -439,3 +441,94 @@ func TestCustomResourceEnqueue(t *testing.T) {
439441 t .Errorf ("Expected the Pod to be attempted 2 times, but got %v" , podInfo .Attempts )
440442 }
441443}
444+
445+ // TestRequeueByBindFailure verify Pods failed by bind plugin are
446+ // put back to the queue regardless of whether event happens or not.
447+ func TestRequeueByBindFailure (t * testing.T ) {
448+ registry := frameworkruntime.Registry {
449+ "firstFailBindPlugin" : newFirstFailBindPlugin ,
450+ }
451+ cfg := configtesting .V1ToInternalWithDefaults (t , configv1.KubeSchedulerConfiguration {
452+ Profiles : []configv1.KubeSchedulerProfile {{
453+ SchedulerName : pointer .String (v1 .DefaultSchedulerName ),
454+ Plugins : & configv1.Plugins {
455+ MultiPoint : configv1.PluginSet {
456+ Enabled : []configv1.Plugin {
457+ {Name : "firstFailBindPlugin" },
458+ },
459+ Disabled : []configv1.Plugin {
460+ {Name : names .DefaultBinder },
461+ },
462+ },
463+ },
464+ }}})
465+
466+ // Use zero backoff seconds to bypass backoffQ.
467+ testCtx := testutils .InitTestSchedulerWithOptions (
468+ t ,
469+ testutils .InitTestAPIServer (t , "core-res-enqueue" , nil ),
470+ 0 ,
471+ scheduler .WithPodInitialBackoffSeconds (0 ),
472+ scheduler .WithPodMaxBackoffSeconds (0 ),
473+ scheduler .WithProfiles (cfg .Profiles ... ),
474+ scheduler .WithFrameworkOutOfTreeRegistry (registry ),
475+ )
476+ testutils .SyncSchedulerInformerFactory (testCtx )
477+
478+ go testCtx .Scheduler .Run (testCtx .Ctx )
479+
480+ cs , ns , ctx := testCtx .ClientSet , testCtx .NS .Name , testCtx .Ctx
481+ node := st .MakeNode ().Name ("fake-node" ).Obj ()
482+ if _ , err := cs .CoreV1 ().Nodes ().Create (ctx , node , metav1.CreateOptions {}); err != nil {
483+ t .Fatalf ("Failed to create Node %q: %v" , node .Name , err )
484+ }
485+ // create a pod.
486+ pod := st .MakePod ().Namespace (ns ).Name ("pod-1" ).Container (imageutils .GetPauseImageName ()).Obj ()
487+ if _ , err := cs .CoreV1 ().Pods (ns ).Create (ctx , pod , metav1.CreateOptions {}); err != nil {
488+ t .Fatalf ("Failed to create Pod %q: %v" , pod .Name , err )
489+ }
490+
491+ // first binding try should fail.
492+ err := wait .Poll (200 * time .Millisecond , wait .ForeverTestTimeout , testutils .PodSchedulingError (cs , ns , "pod-1" ))
493+ if err != nil {
494+ t .Fatalf ("Expect pod-1 to be rejected by the bind plugin" )
495+ }
496+
497+ // The pod should be enqueued to activeQ/backoffQ without any event.
498+ // The pod should be scheduled in the second binding try.
499+ err = wait .Poll (200 * time .Millisecond , wait .ForeverTestTimeout , testutils .PodScheduled (cs , ns , "pod-1" ))
500+ if err != nil {
501+ t .Fatalf ("Expect pod-1 to be scheduled by the bind plugin in the second binding try" )
502+ }
503+ }
504+
505+ // firstFailBindPlugin rejects the Pod in the first Bind call.
506+ type firstFailBindPlugin struct {
507+ counter int
508+ defaultBinderPlugin framework.BindPlugin
509+ }
510+
511+ func newFirstFailBindPlugin (_ runtime.Object , handle framework.Handle ) (framework.Plugin , error ) {
512+ binder , err := defaultbinder .New (nil , handle )
513+ if err != nil {
514+ return nil , err
515+ }
516+
517+ return & firstFailBindPlugin {
518+ defaultBinderPlugin : binder .(framework.BindPlugin ),
519+ }, nil
520+ }
521+
522+ func (* firstFailBindPlugin ) Name () string {
523+ return "firstFailBindPlugin"
524+ }
525+
526+ func (p * firstFailBindPlugin ) Bind (ctx context.Context , state * framework.CycleState , pod * v1.Pod , nodename string ) * framework.Status {
527+ if p .counter == 0 {
528+ // fail in the first Bind call.
529+ p .counter ++
530+ return framework .NewStatus (framework .Error , "firstFailBindPlugin rejects the Pod" )
531+ }
532+
533+ return p .defaultBinderPlugin .Bind (ctx , state , pod , nodename )
534+ }
0 commit comments