k8s.io/kubernetes@v1.29.3/test/integration/scheduler/rescheduling_test.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package scheduler 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 "k8s.io/apimachinery/pkg/util/wait" 26 "k8s.io/klog/v2" 27 "k8s.io/kubernetes/pkg/scheduler" 28 "k8s.io/kubernetes/pkg/scheduler/framework" 29 st "k8s.io/kubernetes/pkg/scheduler/testing" 30 testutils "k8s.io/kubernetes/test/integration/util" 31 ) 32 33 var _ framework.PermitPlugin = &PermitPlugin{} 34 var _ framework.EnqueueExtensions = &PermitPlugin{} 35 var _ framework.ReservePlugin = &ReservePlugin{} 36 var _ framework.EnqueueExtensions = &ReservePlugin{} 37 38 type ReservePlugin struct { 39 name string 40 statusCode framework.Code 41 numReserveCalled int 42 numUnreserveCalled int 43 } 44 45 func (rp *ReservePlugin) Name() string { 46 return rp.name 47 } 48 49 func (rp *ReservePlugin) Reserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status { 50 rp.numReserveCalled += 1 51 52 if rp.statusCode == framework.Error { 53 return framework.NewStatus(framework.Error, "failed to reserve") 54 } 55 56 if rp.statusCode == framework.Unschedulable { 57 if rp.numReserveCalled <= 1 { 58 return framework.NewStatus(framework.Unschedulable, "reject to reserve") 59 } 60 } 61 62 return nil 63 } 64 65 func (rp *ReservePlugin) Unreserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) { 66 rp.numUnreserveCalled += 1 67 } 68 69 func (rp *ReservePlugin) EventsToRegister() []framework.ClusterEventWithHint { 70 return []framework.ClusterEventWithHint{ 71 { 72 Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add}, 73 QueueingHintFn: func(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) { 74 return framework.Queue, nil 75 }, 76 }, 77 } 78 } 79 80 type PermitPlugin struct { 81 name string 82 statusCode framework.Code 83 numPermitCalled int 84 } 85 86 func (pp *PermitPlugin) Name() string { 87 return pp.name 88 } 89 90 func (pp *PermitPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) { 91 pp.numPermitCalled += 1 92 93 if pp.statusCode == framework.Error { 94 return framework.NewStatus(framework.Error, "failed to permit"), 0 95 } 96 97 if pp.statusCode == framework.Unschedulable { 98 if pp.numPermitCalled <= 1 { 99 return framework.NewStatus(framework.Unschedulable, "reject to permit"), 0 100 } 101 } 102 103 return nil, 0 104 } 105 106 func (pp *PermitPlugin) EventsToRegister() []framework.ClusterEventWithHint { 107 return []framework.ClusterEventWithHint{ 108 { 109 Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add}, 110 QueueingHintFn: func(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) { 111 return framework.Queue, nil 112 }, 113 }, 114 } 115 } 116 117 func TestReScheduling(t *testing.T) { 118 testContext := testutils.InitTestAPIServer(t, "permit-plugin", nil) 119 tests := []struct { 120 name string 121 plugins []framework.Plugin 122 action func() error 123 // The first time for pod scheduling, we make pod scheduled error or unschedulable on purpose. 124 // This is controlled by wantFirstSchedulingError. By default, pod is unschedulable. 125 wantFirstSchedulingError bool 126 127 // wantScheduled/wantError means the final expected scheduling result. 128 wantScheduled bool 129 wantError bool 130 }{ 131 { 132 name: "Rescheduling pod rejected by Permit Plugin", 133 plugins: []framework.Plugin{ 134 &PermitPlugin{name: "permit", statusCode: framework.Unschedulable}, 135 }, 136 action: func() error { 137 _, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj()) 138 return err 139 }, 140 wantScheduled: true, 141 }, 142 { 143 name: "Rescheduling pod rejected by Permit Plugin with unrelated event", 144 plugins: []framework.Plugin{ 145 &PermitPlugin{name: "permit", statusCode: framework.Unschedulable}, 146 }, 147 action: func() error { 148 _, err := testutils.CreatePausePod(testContext.ClientSet, 149 testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod-2", Namespace: testContext.NS.Name})) 150 return err 151 }, 152 wantScheduled: false, 153 }, 154 { 155 name: "Rescheduling pod failed by Permit Plugin", 156 plugins: []framework.Plugin{ 157 &PermitPlugin{name: "permit", statusCode: framework.Error}, 158 }, 159 action: func() error { 160 _, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj()) 161 return err 162 }, 163 wantFirstSchedulingError: true, 164 wantError: true, 165 }, 166 { 167 name: "Rescheduling pod rejected by Reserve Plugin", 168 plugins: []framework.Plugin{ 169 &ReservePlugin{name: "reserve", statusCode: framework.Unschedulable}, 170 }, 171 action: func() error { 172 _, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj()) 173 return err 174 }, 175 wantScheduled: true, 176 }, 177 { 178 name: "Rescheduling pod rejected by Reserve Plugin with unrelated event", 179 plugins: []framework.Plugin{ 180 &ReservePlugin{name: "reserve", statusCode: framework.Unschedulable}, 181 }, 182 action: func() error { 183 _, err := testutils.CreatePausePod(testContext.ClientSet, 184 testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod-2", Namespace: testContext.NS.Name})) 185 return err 186 }, 187 wantScheduled: false, 188 }, 189 { 190 name: "Rescheduling pod failed by Reserve Plugin", 191 plugins: []framework.Plugin{ 192 &ReservePlugin{name: "reserve", statusCode: framework.Error}, 193 }, 194 action: func() error { 195 _, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj()) 196 return err 197 }, 198 wantFirstSchedulingError: true, 199 wantError: true, 200 }, 201 } 202 203 for _, test := range tests { 204 t.Run(test.name, func(t *testing.T) { 205 // Create a plugin registry for testing. Register only a permit plugin. 206 registry, prof := InitRegistryAndConfig(t, nil, test.plugins...) 207 208 testCtx, teardown := InitTestSchedulerForFrameworkTest(t, testContext, 2, 209 scheduler.WithProfiles(prof), 210 scheduler.WithFrameworkOutOfTreeRegistry(registry)) 211 defer teardown() 212 213 pod, err := testutils.CreatePausePod(testCtx.ClientSet, 214 testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod", Namespace: testCtx.NS.Name})) 215 if err != nil { 216 t.Errorf("Error while creating a test pod: %v", err) 217 } 218 219 // The first time for scheduling, pod is error or unschedulable, controlled by wantFirstSchedulingError 220 if test.wantFirstSchedulingError { 221 if err = wait.PollUntilContextTimeout(testCtx.Ctx, 10*time.Millisecond, 30*time.Second, false, 222 testutils.PodSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil { 223 t.Errorf("Expected a scheduling error, but got: %v", err) 224 } 225 } else { 226 if err = testutils.WaitForPodUnschedulable(testCtx.ClientSet, pod); err != nil { 227 t.Errorf("Didn't expect the pod to be scheduled. error: %v", err) 228 } 229 } 230 231 if test.action() != nil { 232 if err = test.action(); err != nil { 233 t.Errorf("Perform action() error: %v", err) 234 } 235 } 236 237 if test.wantScheduled { 238 if err = testutils.WaitForPodToSchedule(testCtx.ClientSet, pod); err != nil { 239 t.Errorf("Didn't expect the pod to be unschedulable. error: %v", err) 240 } 241 } else if test.wantError { 242 if err = wait.PollUntilContextTimeout(testCtx.Ctx, 10*time.Millisecond, 30*time.Second, false, 243 testutils.PodSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil { 244 t.Errorf("Expected a scheduling error, but got: %v", err) 245 } 246 } else { 247 if err = testutils.WaitForPodUnschedulable(testCtx.ClientSet, pod); err != nil { 248 t.Errorf("Didn't expect the pod to be scheduled. error: %v", err) 249 } 250 } 251 }) 252 } 253 }