1
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
124
125 wantFirstSchedulingError bool
126
127
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
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
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 }
254
View as plain text