@@ -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