kubernetes/test/integration/scheduler/rescheduling_test.go
kerthcet f5b6f79410 Avoid to use deprecated wait.Poll in scheduler tests
Signed-off-by: kerthcet <kerthcet@gmail.com>
2024-05-10 14:17:09 +08:00

254 lines
8.0 KiB
Go

/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scheduler
import (
"context"
"testing"
"time"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/scheduler"
"k8s.io/kubernetes/pkg/scheduler/framework"
st "k8s.io/kubernetes/pkg/scheduler/testing"
testutils "k8s.io/kubernetes/test/integration/util"
)
var _ framework.PermitPlugin = &PermitPlugin{}
var _ framework.EnqueueExtensions = &PermitPlugin{}
var _ framework.ReservePlugin = &ReservePlugin{}
var _ framework.EnqueueExtensions = &ReservePlugin{}
type ReservePlugin struct {
name string
statusCode framework.Code
numReserveCalled int
numUnreserveCalled int
}
func (rp *ReservePlugin) Name() string {
return rp.name
}
func (rp *ReservePlugin) Reserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
rp.numReserveCalled += 1
if rp.statusCode == framework.Error {
return framework.NewStatus(framework.Error, "failed to reserve")
}
if rp.statusCode == framework.Unschedulable {
if rp.numReserveCalled <= 1 {
return framework.NewStatus(framework.Unschedulable, "reject to reserve")
}
}
return nil
}
func (rp *ReservePlugin) Unreserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) {
rp.numUnreserveCalled += 1
}
func (rp *ReservePlugin) EventsToRegister() []framework.ClusterEventWithHint {
return []framework.ClusterEventWithHint{
{
Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add},
QueueingHintFn: func(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
return framework.Queue, nil
},
},
}
}
type PermitPlugin struct {
name string
statusCode framework.Code
numPermitCalled int
}
func (pp *PermitPlugin) Name() string {
return pp.name
}
func (pp *PermitPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
pp.numPermitCalled += 1
if pp.statusCode == framework.Error {
return framework.NewStatus(framework.Error, "failed to permit"), 0
}
if pp.statusCode == framework.Unschedulable {
if pp.numPermitCalled <= 1 {
return framework.NewStatus(framework.Unschedulable, "reject to permit"), 0
}
}
return nil, 0
}
func (pp *PermitPlugin) EventsToRegister() []framework.ClusterEventWithHint {
return []framework.ClusterEventWithHint{
{
Event: framework.ClusterEvent{Resource: framework.Node, ActionType: framework.Add},
QueueingHintFn: func(logger klog.Logger, pod *v1.Pod, oldObj, newObj interface{}) (framework.QueueingHint, error) {
return framework.Queue, nil
},
},
}
}
func TestReScheduling(t *testing.T) {
testContext := testutils.InitTestAPIServer(t, "permit-plugin", nil)
tests := []struct {
name string
plugins []framework.Plugin
action func() error
// The first time for pod scheduling, we make pod scheduled error or unschedulable on purpose.
// This is controlled by wantFirstSchedulingError. By default, pod is unschedulable.
wantFirstSchedulingError bool
// wantScheduled/wantError means the final expected scheduling result.
wantScheduled bool
wantError bool
}{
{
name: "Rescheduling pod rejected by Permit Plugin",
plugins: []framework.Plugin{
&PermitPlugin{name: "permit", statusCode: framework.Unschedulable},
},
action: func() error {
_, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj())
return err
},
wantScheduled: true,
},
{
name: "Rescheduling pod rejected by Permit Plugin with unrelated event",
plugins: []framework.Plugin{
&PermitPlugin{name: "permit", statusCode: framework.Unschedulable},
},
action: func() error {
_, err := testutils.CreatePausePod(testContext.ClientSet,
testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod-2", Namespace: testContext.NS.Name}))
return err
},
wantScheduled: false,
},
{
name: "Rescheduling pod failed by Permit Plugin",
plugins: []framework.Plugin{
&PermitPlugin{name: "permit", statusCode: framework.Error},
},
action: func() error {
_, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj())
return err
},
wantFirstSchedulingError: true,
wantError: true,
},
{
name: "Rescheduling pod rejected by Reserve Plugin",
plugins: []framework.Plugin{
&ReservePlugin{name: "reserve", statusCode: framework.Unschedulable},
},
action: func() error {
_, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj())
return err
},
wantScheduled: true,
},
{
name: "Rescheduling pod rejected by Reserve Plugin with unrelated event",
plugins: []framework.Plugin{
&ReservePlugin{name: "reserve", statusCode: framework.Unschedulable},
},
action: func() error {
_, err := testutils.CreatePausePod(testContext.ClientSet,
testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod-2", Namespace: testContext.NS.Name}))
return err
},
wantScheduled: false,
},
{
name: "Rescheduling pod failed by Reserve Plugin",
plugins: []framework.Plugin{
&ReservePlugin{name: "reserve", statusCode: framework.Error},
},
action: func() error {
_, err := testutils.CreateNode(testContext.ClientSet, st.MakeNode().Name("fake-node").Obj())
return err
},
wantFirstSchedulingError: true,
wantError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// Create a plugin registry for testing. Register only a permit plugin.
registry, prof := InitRegistryAndConfig(t, nil, test.plugins...)
testCtx, teardown := InitTestSchedulerForFrameworkTest(t, testContext, 2,
scheduler.WithProfiles(prof),
scheduler.WithFrameworkOutOfTreeRegistry(registry))
defer teardown()
pod, err := testutils.CreatePausePod(testCtx.ClientSet,
testutils.InitPausePod(&testutils.PausePodConfig{Name: "test-pod", Namespace: testCtx.NS.Name}))
if err != nil {
t.Errorf("Error while creating a test pod: %v", err)
}
// The first time for scheduling, pod is error or unschedulable, controlled by wantFirstSchedulingError
if test.wantFirstSchedulingError {
if err = wait.PollUntilContextTimeout(testCtx.Ctx, 10*time.Millisecond, 30*time.Second, false,
testutils.PodSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil {
t.Errorf("Expected a scheduling error, but got: %v", err)
}
} else {
if err = testutils.WaitForPodUnschedulable(testCtx.Ctx, testCtx.ClientSet, pod); err != nil {
t.Errorf("Didn't expect the pod to be scheduled. error: %v", err)
}
}
if test.action() != nil {
if err = test.action(); err != nil {
t.Errorf("Perform action() error: %v", err)
}
}
if test.wantScheduled {
if err = testutils.WaitForPodToSchedule(testCtx.ClientSet, pod); err != nil {
t.Errorf("Didn't expect the pod to be unschedulable. error: %v", err)
}
} else if test.wantError {
if err = wait.PollUntilContextTimeout(testCtx.Ctx, 10*time.Millisecond, 30*time.Second, false,
testutils.PodSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil {
t.Errorf("Expected a scheduling error, but got: %v", err)
}
} else {
if err = testutils.WaitForPodUnschedulable(testCtx.Ctx, testCtx.ClientSet, pod); err != nil {
t.Errorf("Didn't expect the pod to be scheduled. error: %v", err)
}
}
})
}
}