...

Source file src/k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread/plugin_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread

     1  /*
     2  Copyright 2023 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 podtopologyspread
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  	v1 "k8s.io/api/core/v1"
    24  	"k8s.io/klog/v2/ktesting"
    25  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    26  	"k8s.io/kubernetes/pkg/scheduler/framework"
    27  	plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing"
    28  	"k8s.io/kubernetes/pkg/scheduler/internal/cache"
    29  	st "k8s.io/kubernetes/pkg/scheduler/testing"
    30  )
    31  
    32  func Test_isSchedulableAfterNodeChange(t *testing.T) {
    33  	testcases := []struct {
    34  		name             string
    35  		pod              *v1.Pod
    36  		oldNode, newNode *v1.Node
    37  		expectedHint     framework.QueueingHint
    38  		expectedErr      bool
    39  	}{
    40  		{
    41  			name: "node updates label which matches topologyKey",
    42  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    43  				Obj(),
    44  			oldNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
    45  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone2").Obj(),
    46  			expectedHint: framework.Queue,
    47  		},
    48  		{
    49  			name: "node that doesn't match topologySpreadConstraints updates non-related label",
    50  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    51  				Obj(),
    52  			oldNode:      st.MakeNode().Name("node-a").Label("foo", "bar1").Obj(),
    53  			newNode:      st.MakeNode().Name("node-a").Label("foo", "bar2").Obj(),
    54  			expectedHint: framework.QueueSkip,
    55  		},
    56  		{
    57  			name: "node that match topologySpreadConstraints adds non-related label",
    58  			pod: st.MakePod().Name("p").
    59  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    60  				SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    61  				Obj(),
    62  			oldNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node1").Label("foo", "bar").Obj(),
    63  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node1").Obj(),
    64  			expectedHint: framework.Queue,
    65  		},
    66  		{
    67  			name: "create node with non-related labels",
    68  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    69  				Obj(),
    70  			newNode:      st.MakeNode().Name("node-a").Label("foo", "bar").Obj(),
    71  			expectedHint: framework.QueueSkip,
    72  		},
    73  		{
    74  			name: "create node with related labels",
    75  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    76  				Obj(),
    77  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
    78  			expectedHint: framework.Queue,
    79  		},
    80  		{
    81  			name: "delete node with non-related labels",
    82  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    83  				Obj(),
    84  			oldNode:      st.MakeNode().Name("node-a").Label("foo", "bar").Obj(),
    85  			expectedHint: framework.QueueSkip,
    86  		},
    87  		{
    88  			name: "delete node with related labels",
    89  			pod: st.MakePod().Name("p").SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    90  				Obj(),
    91  			oldNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
    92  			expectedHint: framework.Queue,
    93  		},
    94  		{
    95  			name: "add node with related labels that only match one of topologySpreadConstraints",
    96  			pod: st.MakePod().Name("p").
    97  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    98  				SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
    99  				Obj(),
   100  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
   101  			expectedHint: framework.QueueSkip,
   102  		},
   103  		{
   104  			name: "add node with related labels that match all topologySpreadConstraints",
   105  			pod: st.MakePod().Name("p").
   106  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   107  				SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   108  				Obj(),
   109  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node1").Obj(),
   110  			expectedHint: framework.Queue,
   111  		},
   112  		{
   113  			name: "update node with related labels that only match one of topologySpreadConstraints",
   114  			pod: st.MakePod().Name("p").
   115  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   116  				SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   117  				Obj(),
   118  			oldNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
   119  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(),
   120  			expectedHint: framework.QueueSkip,
   121  		},
   122  		{
   123  			name: "update node with related labels that match all topologySpreadConstraints",
   124  			pod: st.MakePod().Name("p").
   125  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   126  				SpreadConstraint(1, "node", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   127  				Obj(),
   128  			oldNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node1").Obj(),
   129  			newNode:      st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node2").Obj(),
   130  			expectedHint: framework.Queue,
   131  		},
   132  	}
   133  
   134  	for _, tc := range testcases {
   135  		t.Run(tc.name, func(t *testing.T) {
   136  			logger, ctx := ktesting.NewTestContext(t)
   137  			snapshot := cache.NewSnapshot(nil, nil)
   138  			pl := plugintesting.SetupPlugin(ctx, t, topologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.ListDefaulting}, snapshot)
   139  			p := pl.(*PodTopologySpread)
   140  			actualHint, err := p.isSchedulableAfterNodeChange(logger, tc.pod, tc.oldNode, tc.newNode)
   141  			if tc.expectedErr {
   142  				require.Error(t, err)
   143  				return
   144  			}
   145  			require.NoError(t, err)
   146  			require.Equal(t, tc.expectedHint, actualHint)
   147  		})
   148  	}
   149  }
   150  
   151  func Test_isSchedulableAfterPodChange(t *testing.T) {
   152  	testcases := []struct {
   153  		name           string
   154  		pod            *v1.Pod
   155  		oldPod, newPod *v1.Pod
   156  		expectedHint   framework.QueueingHint
   157  		expectedErr    bool
   158  	}{
   159  		{
   160  			name: "add pod with labels match topologySpreadConstraints selector",
   161  			pod: st.MakePod().Name("p").Label("foo", "").
   162  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   163  				Obj(),
   164  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   165  			expectedHint: framework.Queue,
   166  		},
   167  		{
   168  			name: "add un-scheduled pod",
   169  			pod: st.MakePod().Name("p").Label("foo", "").
   170  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   171  				Obj(),
   172  			newPod:       st.MakePod().Label("foo", "").Obj(),
   173  			expectedHint: framework.QueueSkip,
   174  		},
   175  		{
   176  			name: "update un-scheduled pod",
   177  			pod: st.MakePod().Name("p").Label("foo", "").
   178  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   179  				Obj(),
   180  			newPod:       st.MakePod().Label("foo", "").Obj(),
   181  			oldPod:       st.MakePod().Label("bar", "").Obj(),
   182  			expectedHint: framework.QueueSkip,
   183  		},
   184  		{
   185  			name: "delete un-scheduled pod",
   186  			pod: st.MakePod().Name("p").Label("foo", "").
   187  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   188  				Obj(),
   189  			oldPod:       st.MakePod().Label("foo", "").Obj(),
   190  			expectedHint: framework.QueueSkip,
   191  		},
   192  		{
   193  			name: "add pod with different namespace",
   194  			pod: st.MakePod().Name("p").Label("foo", "").
   195  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   196  				Obj(),
   197  			newPod:       st.MakePod().Node("fake-node").Namespace("fake-namespace").Label("foo", "").Obj(),
   198  			expectedHint: framework.QueueSkip,
   199  		},
   200  		{
   201  			name: "add pod with labels don't match topologySpreadConstraints selector",
   202  			pod: st.MakePod().Name("p").Label("foo", "").
   203  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   204  				Obj(),
   205  			newPod:       st.MakePod().Node("fake-node").Label("bar", "").Obj(),
   206  			expectedHint: framework.QueueSkip,
   207  		},
   208  		{
   209  			name: "delete pod with labels that match topologySpreadConstraints selector",
   210  			pod: st.MakePod().Name("p").Label("foo", "").
   211  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   212  				Obj(),
   213  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   214  			expectedHint: framework.Queue,
   215  		},
   216  		{
   217  			name: "delete pod with labels that don't match topologySpreadConstraints selector",
   218  			pod: st.MakePod().Name("p").Label("foo", "").
   219  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   220  				Obj(),
   221  			oldPod:       st.MakePod().Node("fake-node").Label("bar", "").Obj(),
   222  			expectedHint: framework.QueueSkip,
   223  		},
   224  		{
   225  			name: "update pod's non-related label",
   226  			pod: st.MakePod().Name("p").Label("foo", "").
   227  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   228  				Obj(),
   229  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar1").Obj(),
   230  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar2").Obj(),
   231  			expectedHint: framework.QueueSkip,
   232  		},
   233  		{
   234  			name: "add pod's label that matches topologySpreadConstraints selector",
   235  			pod: st.MakePod().Name("p").Label("foo", "").
   236  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   237  				Obj(),
   238  			oldPod:       st.MakePod().Node("fake-node").Obj(),
   239  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   240  			expectedHint: framework.Queue,
   241  		},
   242  		{
   243  			name: "delete pod label that matches topologySpreadConstraints selector",
   244  			pod: st.MakePod().Name("p").Label("foo", "").
   245  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   246  				Obj(),
   247  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   248  			newPod:       st.MakePod().Node("fake-node").Obj(),
   249  			expectedHint: framework.Queue,
   250  		},
   251  		{
   252  			name: "change pod's label that matches topologySpreadConstraints selector",
   253  			pod: st.MakePod().Name("p").Label("foo", "").
   254  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   255  				Obj(),
   256  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "foo1").Obj(),
   257  			newPod:       st.MakePod().Node("fake-node").Label("foo", "foo2").Obj(),
   258  			expectedHint: framework.QueueSkip,
   259  		},
   260  		{
   261  			name: "change pod's label that doesn't match topologySpreadConstraints selector",
   262  			pod: st.MakePod().Name("p").Label("foo", "").
   263  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   264  				Obj(),
   265  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar1").Obj(),
   266  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar2").Obj(),
   267  			expectedHint: framework.QueueSkip,
   268  		},
   269  		{
   270  			name: "add pod's label that matches topologySpreadConstraints selector with multi topologySpreadConstraints",
   271  			pod: st.MakePod().Name("p").Label("foo", "").
   272  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   273  				SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
   274  				Obj(),
   275  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   276  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar2").Obj(),
   277  			expectedHint: framework.Queue,
   278  		},
   279  		{
   280  			name: "change pod's label that doesn't match topologySpreadConstraints selector with multi topologySpreadConstraints",
   281  			pod: st.MakePod().Name("p").Label("foo", "").
   282  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   283  				SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
   284  				Obj(),
   285  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Obj(),
   286  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("baz", "").Obj(),
   287  			expectedHint: framework.QueueSkip,
   288  		},
   289  		{
   290  			name: "change pod's label that match topologySpreadConstraints selector with multi topologySpreadConstraints",
   291  			pod: st.MakePod().Name("p").Label("foo", "").
   292  				SpreadConstraint(1, "zone", v1.DoNotSchedule, fooSelector, nil, nil, nil, nil).
   293  				SpreadConstraint(1, "node", v1.DoNotSchedule, barSelector, nil, nil, nil, nil).
   294  				Obj(),
   295  			oldPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "").Obj(),
   296  			newPod:       st.MakePod().Node("fake-node").Label("foo", "").Label("bar", "bar2").Obj(),
   297  			expectedHint: framework.QueueSkip,
   298  		},
   299  	}
   300  	for _, tc := range testcases {
   301  		t.Run(tc.name, func(t *testing.T) {
   302  			logger, ctx := ktesting.NewTestContext(t)
   303  			snapshot := cache.NewSnapshot(nil, nil)
   304  			pl := plugintesting.SetupPlugin(ctx, t, topologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.ListDefaulting}, snapshot)
   305  			p := pl.(*PodTopologySpread)
   306  			actualHint, err := p.isSchedulableAfterPodChange(logger, tc.pod, tc.oldPod, tc.newPod)
   307  			if tc.expectedErr {
   308  				require.Error(t, err)
   309  				return
   310  			}
   311  			require.NoError(t, err)
   312  			require.Equal(t, tc.expectedHint, actualHint)
   313  		})
   314  	}
   315  }
   316  

View as plain text