@@ -45,6 +45,7 @@ import (
45
45
"k8s.io/kubernetes/pkg/scheduler/framework"
46
46
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
47
47
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
48
+ "k8s.io/kubernetes/pkg/scheduler/framework/plugins/schedulinggates"
48
49
frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
49
50
st "k8s.io/kubernetes/pkg/scheduler/testing"
50
51
schedulerutils "k8s.io/kubernetes/test/integration/scheduler"
@@ -2588,3 +2589,197 @@ func TestActivatePods(t *testing.T) {
2588
2589
t .Errorf ("JobPlugin's pods activation logic is not called" )
2589
2590
}
2590
2591
}
2592
+
2593
+ var _ framework.PreEnqueuePlugin = & SchedulingGatesPluginWithEvents {}
2594
+ var _ framework.EnqueueExtensions = & SchedulingGatesPluginWithEvents {}
2595
+ var _ framework.PreEnqueuePlugin = & SchedulingGatesPluginWOEvents {}
2596
+ var _ framework.EnqueueExtensions = & SchedulingGatesPluginWOEvents {}
2597
+
2598
+ const (
2599
+ schedulingGatesPluginWithEvents = "scheduling-gates-with-events"
2600
+ schedulingGatesPluginWOEvents = "scheduling-gates-without-events"
2601
+ )
2602
+
2603
+ type SchedulingGatesPluginWithEvents struct {
2604
+ called int
2605
+ schedulinggates.SchedulingGates
2606
+ }
2607
+
2608
+ func (pl * SchedulingGatesPluginWithEvents ) Name () string {
2609
+ return schedulingGatesPluginWithEvents
2610
+ }
2611
+
2612
+ func (pl * SchedulingGatesPluginWithEvents ) PreEnqueue (ctx context.Context , p * v1.Pod ) * framework.Status {
2613
+ pl .called ++
2614
+ return pl .SchedulingGates .PreEnqueue (ctx , p )
2615
+ }
2616
+
2617
+ func (pl * SchedulingGatesPluginWithEvents ) EventsToRegister () []framework.ClusterEventWithHint {
2618
+ return []framework.ClusterEventWithHint {
2619
+ {Event : framework.ClusterEvent {Resource : framework .Pod , ActionType : framework .Update }},
2620
+ }
2621
+ }
2622
+
2623
+ type SchedulingGatesPluginWOEvents struct {
2624
+ called int
2625
+ schedulinggates.SchedulingGates
2626
+ }
2627
+
2628
+ func (pl * SchedulingGatesPluginWOEvents ) Name () string {
2629
+ return schedulingGatesPluginWOEvents
2630
+ }
2631
+
2632
+ func (pl * SchedulingGatesPluginWOEvents ) PreEnqueue (ctx context.Context , p * v1.Pod ) * framework.Status {
2633
+ pl .called ++
2634
+ return pl .SchedulingGates .PreEnqueue (ctx , p )
2635
+ }
2636
+
2637
+ func (pl * SchedulingGatesPluginWOEvents ) EventsToRegister () []framework.ClusterEventWithHint {
2638
+ return nil
2639
+ }
2640
+
2641
+ // This test helps to verify registering nil events for schedulingGates plugin works as expected.
2642
+ func TestSchedulingGatesPluginEventsToRegister (t * testing.T ) {
2643
+ defer featuregatetesting .SetFeatureGateDuringTest (t , feature .DefaultFeatureGate , features .PodSchedulingReadiness , true )()
2644
+
2645
+ testContext := testutils .InitTestAPIServer (t , "preenqueue-plugin" , nil )
2646
+
2647
+ num := func (pl framework.Plugin ) int {
2648
+ switch item := pl .(type ) {
2649
+ case * SchedulingGatesPluginWithEvents :
2650
+ return item .called
2651
+ case * SchedulingGatesPluginWOEvents :
2652
+ return item .called
2653
+ default :
2654
+ t .Error ("unsupported plugin" )
2655
+ }
2656
+ return 0
2657
+ }
2658
+
2659
+ tests := []struct {
2660
+ name string
2661
+ enqueuePlugin framework.PreEnqueuePlugin
2662
+ count int
2663
+ }{
2664
+ {
2665
+ name : "preEnqueue plugin without event registered" ,
2666
+ enqueuePlugin : & SchedulingGatesPluginWOEvents {SchedulingGates : schedulinggates.SchedulingGates {EnablePodSchedulingReadiness : true }},
2667
+ count : 2 ,
2668
+ },
2669
+ {
2670
+ name : "preEnqueue plugin with event registered" ,
2671
+ enqueuePlugin : & SchedulingGatesPluginWithEvents {SchedulingGates : schedulinggates.SchedulingGates {EnablePodSchedulingReadiness : true }},
2672
+ count : 3 ,
2673
+ },
2674
+ }
2675
+
2676
+ for _ , tt := range tests {
2677
+ t .Run (tt .name , func (t * testing.T ) {
2678
+ registry := frameworkruntime.Registry {
2679
+ tt .enqueuePlugin .Name (): newPlugin (tt .enqueuePlugin ),
2680
+ }
2681
+
2682
+ // Setup plugins for testing.
2683
+ cfg := configtesting .V1ToInternalWithDefaults (t , configv1.KubeSchedulerConfiguration {
2684
+ Profiles : []configv1.KubeSchedulerProfile {{
2685
+ SchedulerName : pointer .String (v1 .DefaultSchedulerName ),
2686
+ Plugins : & configv1.Plugins {
2687
+ PreEnqueue : configv1.PluginSet {
2688
+ Enabled : []configv1.Plugin {
2689
+ {Name : tt .enqueuePlugin .Name ()},
2690
+ },
2691
+ Disabled : []configv1.Plugin {
2692
+ {Name : "*" },
2693
+ },
2694
+ },
2695
+ },
2696
+ }},
2697
+ })
2698
+
2699
+ testCtx , teardown := schedulerutils .InitTestSchedulerForFrameworkTest (t , testContext , 2 ,
2700
+ scheduler .WithProfiles (cfg .Profiles ... ),
2701
+ scheduler .WithFrameworkOutOfTreeRegistry (registry ),
2702
+ )
2703
+ defer teardown ()
2704
+
2705
+ // Create a pod with schedulingGates.
2706
+ gatedPod := st .MakePod ().Name ("p" ).Namespace (testContext .NS .Name ).
2707
+ SchedulingGates ([]string {"foo" }).
2708
+ PodAffinity ("kubernetes.io/hostname" , & metav1.LabelSelector {MatchLabels : map [string ]string {"foo" : "bar" }}, st .PodAffinityWithRequiredReq ).
2709
+ Container ("pause" ).Obj ()
2710
+ gatedPod , err := testutils .CreatePausePod (testCtx .ClientSet , gatedPod )
2711
+ if err != nil {
2712
+ t .Errorf ("Error while creating a gated pod: %v" , err )
2713
+ return
2714
+ }
2715
+
2716
+ if err := testutils .WaitForPodSchedulingGated (testCtx .ClientSet , gatedPod , 10 * time .Second ); err != nil {
2717
+ t .Errorf ("Expected the pod to be gated, but got: %v" , err )
2718
+ return
2719
+ }
2720
+ if num (tt .enqueuePlugin ) != 1 {
2721
+ t .Errorf ("Expected the preEnqueue plugin to be called once, but got %v" , num (tt .enqueuePlugin ))
2722
+ return
2723
+ }
2724
+
2725
+ // Create a best effort pod.
2726
+ pausePod , err := testutils .CreatePausePod (testCtx .ClientSet , testutils .InitPausePod (& testutils.PausePodConfig {
2727
+ Name : "pause-pod" ,
2728
+ Namespace : testCtx .NS .Name ,
2729
+ Labels : map [string ]string {"foo" : "bar" },
2730
+ }))
2731
+ if err != nil {
2732
+ t .Errorf ("Error while creating a pod: %v" , err )
2733
+ return
2734
+ }
2735
+
2736
+ // Wait for the pod schedulabled.
2737
+ if err := testutils .WaitForPodToScheduleWithTimeout (testCtx .ClientSet , pausePod , 10 * time .Second ); err != nil {
2738
+ t .Errorf ("Expected the pod to be schedulable, but got: %v" , err )
2739
+ return
2740
+ }
2741
+
2742
+ // Update the pod which will trigger the requeue logic if plugin registers the events.
2743
+ pausePod , err = testCtx .ClientSet .CoreV1 ().Pods (pausePod .Namespace ).Get (testCtx .Ctx , pausePod .Name , metav1.GetOptions {})
2744
+ if err != nil {
2745
+ t .Errorf ("Error while getting a pod: %v" , err )
2746
+ return
2747
+ }
2748
+ pausePod .Annotations = map [string ]string {"foo" : "bar" }
2749
+ _ , err = testCtx .ClientSet .CoreV1 ().Pods (pausePod .Namespace ).Update (testCtx .Ctx , pausePod , metav1.UpdateOptions {})
2750
+ if err != nil {
2751
+ t .Errorf ("Error while updating a pod: %v" , err )
2752
+ return
2753
+ }
2754
+
2755
+ // Pod should still be unschedulable because scheduling gates still exist, theoretically, it's a waste rescheduling.
2756
+ if err := testutils .WaitForPodSchedulingGated (testCtx .ClientSet , gatedPod , 10 * time .Second ); err != nil {
2757
+ t .Errorf ("Expected the pod to be gated, but got: %v" , err )
2758
+ return
2759
+ }
2760
+ if num (tt .enqueuePlugin ) != tt .count {
2761
+ t .Errorf ("Expected the preEnqueue plugin to be called %v, but got %v" , tt .count , num (tt .enqueuePlugin ))
2762
+ return
2763
+ }
2764
+
2765
+ // Remove gated pod's scheduling gates.
2766
+ gatedPod , err = testCtx .ClientSet .CoreV1 ().Pods (gatedPod .Namespace ).Get (testCtx .Ctx , gatedPod .Name , metav1.GetOptions {})
2767
+ if err != nil {
2768
+ t .Errorf ("Error while getting a pod: %v" , err )
2769
+ return
2770
+ }
2771
+ gatedPod .Spec .SchedulingGates = nil
2772
+ _ , err = testCtx .ClientSet .CoreV1 ().Pods (gatedPod .Namespace ).Update (testCtx .Ctx , gatedPod , metav1.UpdateOptions {})
2773
+ if err != nil {
2774
+ t .Errorf ("Error while updating a pod: %v" , err )
2775
+ return
2776
+ }
2777
+
2778
+ // Ungated pod should be schedulable now.
2779
+ if err := testutils .WaitForPodToScheduleWithTimeout (testCtx .ClientSet , gatedPod , 10 * time .Second ); err != nil {
2780
+ t .Errorf ("Expected the pod to be schedulable, but got: %v" , err )
2781
+ return
2782
+ }
2783
+ })
2784
+ }
2785
+ }
0 commit comments