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  }