k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/scheduler/apis/config/v1/default_plugins_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 v1
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	"k8s.io/apiserver/pkg/util/feature"
    24  	"k8s.io/component-base/featuregate"
    25  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    26  	"k8s.io/klog/v2/ktesting"
    27  	v1 "k8s.io/kube-scheduler/config/v1"
    28  	"k8s.io/kubernetes/pkg/features"
    29  	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/names"
    30  	"k8s.io/utils/ptr"
    31  )
    32  
    33  func TestApplyFeatureGates(t *testing.T) {
    34  	tests := []struct {
    35  		name       string
    36  		features   map[featuregate.Feature]bool
    37  		wantConfig *v1.Plugins
    38  	}{
    39  		{
    40  			name: "Feature gate DynamicResourceAllocation disabled",
    41  			features: map[featuregate.Feature]bool{
    42  				features.DynamicResourceAllocation: false,
    43  			},
    44  			wantConfig: &v1.Plugins{
    45  				MultiPoint: v1.PluginSet{
    46  					Enabled: []v1.Plugin{
    47  						{Name: names.SchedulingGates},
    48  						{Name: names.PrioritySort},
    49  						{Name: names.NodeUnschedulable},
    50  						{Name: names.NodeName},
    51  						{Name: names.TaintToleration, Weight: ptr.To[int32](3)},
    52  						{Name: names.NodeAffinity, Weight: ptr.To[int32](2)},
    53  						{Name: names.NodePorts},
    54  						{Name: names.NodeResourcesFit, Weight: ptr.To[int32](1)},
    55  						{Name: names.VolumeRestrictions},
    56  						{Name: names.NodeVolumeLimits},
    57  						{Name: names.VolumeBinding},
    58  						{Name: names.VolumeZone},
    59  						{Name: names.PodTopologySpread, Weight: ptr.To[int32](2)},
    60  						{Name: names.InterPodAffinity, Weight: ptr.To[int32](2)},
    61  						{Name: names.DefaultPreemption},
    62  						{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
    63  						{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
    64  						{Name: names.DefaultBinder},
    65  					},
    66  				},
    67  			},
    68  		},
    69  		{
    70  			name: "Feature gate DynamicResourceAllocation enabled",
    71  			features: map[featuregate.Feature]bool{
    72  				features.DynamicResourceAllocation: true,
    73  			},
    74  			wantConfig: &v1.Plugins{
    75  				MultiPoint: v1.PluginSet{
    76  					Enabled: []v1.Plugin{
    77  						{Name: names.SchedulingGates},
    78  						{Name: names.PrioritySort},
    79  						{Name: names.NodeUnschedulable},
    80  						{Name: names.NodeName},
    81  						{Name: names.TaintToleration, Weight: ptr.To[int32](3)},
    82  						{Name: names.NodeAffinity, Weight: ptr.To[int32](2)},
    83  						{Name: names.NodePorts},
    84  						{Name: names.NodeResourcesFit, Weight: ptr.To[int32](1)},
    85  						{Name: names.VolumeRestrictions},
    86  						{Name: names.NodeVolumeLimits},
    87  						{Name: names.VolumeBinding},
    88  						{Name: names.VolumeZone},
    89  						{Name: names.PodTopologySpread, Weight: ptr.To[int32](2)},
    90  						{Name: names.InterPodAffinity, Weight: ptr.To[int32](2)},
    91  						{Name: names.DynamicResources},
    92  						{Name: names.DefaultPreemption},
    93  						{Name: names.NodeResourcesBalancedAllocation, Weight: ptr.To[int32](1)},
    94  						{Name: names.ImageLocality, Weight: ptr.To[int32](1)},
    95  						{Name: names.DefaultBinder},
    96  					},
    97  				},
    98  			},
    99  		},
   100  	}
   101  
   102  	for _, test := range tests {
   103  		t.Run(test.name, func(t *testing.T) {
   104  			for k, v := range test.features {
   105  				featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, k, v)
   106  			}
   107  
   108  			gotConfig := getDefaultPlugins()
   109  			if diff := cmp.Diff(test.wantConfig, gotConfig); diff != "" {
   110  				t.Errorf("unexpected config diff (-want, +got): %s", diff)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func TestMergePlugins(t *testing.T) {
   117  	tests := []struct {
   118  		name            string
   119  		customPlugins   *v1.Plugins
   120  		defaultPlugins  *v1.Plugins
   121  		expectedPlugins *v1.Plugins
   122  	}{
   123  		{
   124  			name: "AppendCustomPlugin",
   125  			customPlugins: &v1.Plugins{
   126  				Filter: v1.PluginSet{
   127  					Enabled: []v1.Plugin{
   128  						{Name: "CustomPlugin"},
   129  					},
   130  				},
   131  			},
   132  			defaultPlugins: &v1.Plugins{
   133  				Filter: v1.PluginSet{
   134  					Enabled: []v1.Plugin{
   135  						{Name: "DefaultPlugin1"},
   136  						{Name: "DefaultPlugin2"},
   137  					},
   138  				},
   139  			},
   140  			expectedPlugins: &v1.Plugins{
   141  				Filter: v1.PluginSet{
   142  					Enabled: []v1.Plugin{
   143  						{Name: "DefaultPlugin1"},
   144  						{Name: "DefaultPlugin2"},
   145  						{Name: "CustomPlugin"},
   146  					},
   147  				},
   148  			},
   149  		},
   150  		{
   151  			name: "InsertAfterDefaultPlugins2",
   152  			customPlugins: &v1.Plugins{
   153  				Filter: v1.PluginSet{
   154  					Enabled: []v1.Plugin{
   155  						{Name: "CustomPlugin"},
   156  						{Name: "DefaultPlugin2"},
   157  					},
   158  					Disabled: []v1.Plugin{
   159  						{Name: "DefaultPlugin2"},
   160  					},
   161  				},
   162  			},
   163  			defaultPlugins: &v1.Plugins{
   164  				Filter: v1.PluginSet{
   165  					Enabled: []v1.Plugin{
   166  						{Name: "DefaultPlugin1"},
   167  						{Name: "DefaultPlugin2"},
   168  					},
   169  				},
   170  			},
   171  			expectedPlugins: &v1.Plugins{
   172  				Filter: v1.PluginSet{
   173  					Enabled: []v1.Plugin{
   174  						{Name: "DefaultPlugin1"},
   175  						{Name: "CustomPlugin"},
   176  						{Name: "DefaultPlugin2"},
   177  					},
   178  					Disabled: []v1.Plugin{
   179  						{Name: "DefaultPlugin2"},
   180  					},
   181  				},
   182  			},
   183  		},
   184  		{
   185  			name: "InsertBeforeAllPlugins",
   186  			customPlugins: &v1.Plugins{
   187  				Filter: v1.PluginSet{
   188  					Enabled: []v1.Plugin{
   189  						{Name: "CustomPlugin"},
   190  						{Name: "DefaultPlugin1"},
   191  						{Name: "DefaultPlugin2"},
   192  					},
   193  					Disabled: []v1.Plugin{
   194  						{Name: "*"},
   195  					},
   196  				},
   197  			},
   198  			defaultPlugins: &v1.Plugins{
   199  				Filter: v1.PluginSet{
   200  					Enabled: []v1.Plugin{
   201  						{Name: "DefaultPlugin1"},
   202  						{Name: "DefaultPlugin2"},
   203  					},
   204  				},
   205  			},
   206  			expectedPlugins: &v1.Plugins{
   207  				Filter: v1.PluginSet{
   208  					Enabled: []v1.Plugin{
   209  						{Name: "CustomPlugin"},
   210  						{Name: "DefaultPlugin1"},
   211  						{Name: "DefaultPlugin2"},
   212  					},
   213  					Disabled: []v1.Plugin{
   214  						{Name: "*"},
   215  					},
   216  				},
   217  			},
   218  		},
   219  		{
   220  			name: "ReorderDefaultPlugins",
   221  			customPlugins: &v1.Plugins{
   222  				Filter: v1.PluginSet{
   223  					Enabled: []v1.Plugin{
   224  						{Name: "DefaultPlugin2"},
   225  						{Name: "DefaultPlugin1"},
   226  					},
   227  					Disabled: []v1.Plugin{
   228  						{Name: "*"},
   229  					},
   230  				},
   231  			},
   232  			defaultPlugins: &v1.Plugins{
   233  				Filter: v1.PluginSet{
   234  					Enabled: []v1.Plugin{
   235  						{Name: "DefaultPlugin1"},
   236  						{Name: "DefaultPlugin2"},
   237  					},
   238  				},
   239  			},
   240  			expectedPlugins: &v1.Plugins{
   241  				Filter: v1.PluginSet{
   242  					Enabled: []v1.Plugin{
   243  						{Name: "DefaultPlugin2"},
   244  						{Name: "DefaultPlugin1"},
   245  					},
   246  					Disabled: []v1.Plugin{
   247  						{Name: "*"},
   248  					},
   249  				},
   250  			},
   251  		},
   252  		{
   253  			name:          "ApplyNilCustomPlugin",
   254  			customPlugins: nil,
   255  			defaultPlugins: &v1.Plugins{
   256  				Filter: v1.PluginSet{
   257  					Enabled: []v1.Plugin{
   258  						{Name: "DefaultPlugin1"},
   259  						{Name: "DefaultPlugin2"},
   260  					},
   261  				},
   262  			},
   263  			expectedPlugins: &v1.Plugins{
   264  				Filter: v1.PluginSet{
   265  					Enabled: []v1.Plugin{
   266  						{Name: "DefaultPlugin1"},
   267  						{Name: "DefaultPlugin2"},
   268  					},
   269  				},
   270  			},
   271  		},
   272  		{
   273  			name: "CustomPluginOverrideDefaultPlugin",
   274  			customPlugins: &v1.Plugins{
   275  				Filter: v1.PluginSet{
   276  					Enabled: []v1.Plugin{
   277  						{Name: "Plugin1", Weight: ptr.To[int32](2)},
   278  						{Name: "Plugin3", Weight: ptr.To[int32](3)},
   279  					},
   280  				},
   281  			},
   282  			defaultPlugins: &v1.Plugins{
   283  				Filter: v1.PluginSet{
   284  					Enabled: []v1.Plugin{
   285  						{Name: "Plugin1"},
   286  						{Name: "Plugin2"},
   287  						{Name: "Plugin3"},
   288  					},
   289  				},
   290  			},
   291  			expectedPlugins: &v1.Plugins{
   292  				Filter: v1.PluginSet{
   293  					Enabled: []v1.Plugin{
   294  						{Name: "Plugin1", Weight: ptr.To[int32](2)},
   295  						{Name: "Plugin2"},
   296  						{Name: "Plugin3", Weight: ptr.To[int32](3)},
   297  					},
   298  				},
   299  			},
   300  		},
   301  		{
   302  			name: "OrderPreserveAfterOverride",
   303  			customPlugins: &v1.Plugins{
   304  				Filter: v1.PluginSet{
   305  					Enabled: []v1.Plugin{
   306  						{Name: "Plugin2", Weight: ptr.To[int32](2)},
   307  						{Name: "Plugin1", Weight: ptr.To[int32](1)},
   308  					},
   309  				},
   310  			},
   311  			defaultPlugins: &v1.Plugins{
   312  				Filter: v1.PluginSet{
   313  					Enabled: []v1.Plugin{
   314  						{Name: "Plugin1"},
   315  						{Name: "Plugin2"},
   316  						{Name: "Plugin3"},
   317  					},
   318  				},
   319  			},
   320  			expectedPlugins: &v1.Plugins{
   321  				Filter: v1.PluginSet{
   322  					Enabled: []v1.Plugin{
   323  						{Name: "Plugin1", Weight: ptr.To[int32](1)},
   324  						{Name: "Plugin2", Weight: ptr.To[int32](2)},
   325  						{Name: "Plugin3"},
   326  					},
   327  				},
   328  			},
   329  		},
   330  		{
   331  			name: "RepeatedCustomPlugin",
   332  			customPlugins: &v1.Plugins{
   333  				Filter: v1.PluginSet{
   334  					Enabled: []v1.Plugin{
   335  						{Name: "Plugin1"},
   336  						{Name: "Plugin2", Weight: ptr.To[int32](2)},
   337  						{Name: "Plugin3"},
   338  						{Name: "Plugin2", Weight: ptr.To[int32](4)},
   339  					},
   340  				},
   341  			},
   342  			defaultPlugins: &v1.Plugins{
   343  				Filter: v1.PluginSet{
   344  					Enabled: []v1.Plugin{
   345  						{Name: "Plugin1"},
   346  						{Name: "Plugin2"},
   347  						{Name: "Plugin3"},
   348  					},
   349  				},
   350  			},
   351  			expectedPlugins: &v1.Plugins{
   352  				Filter: v1.PluginSet{
   353  					Enabled: []v1.Plugin{
   354  						{Name: "Plugin1"},
   355  						{Name: "Plugin2", Weight: ptr.To[int32](4)},
   356  						{Name: "Plugin3"},
   357  						{Name: "Plugin2", Weight: ptr.To[int32](2)},
   358  					},
   359  				},
   360  			},
   361  		},
   362  		{
   363  			name: "Append custom MultiPoint plugin",
   364  			customPlugins: &v1.Plugins{
   365  				MultiPoint: v1.PluginSet{
   366  					Enabled: []v1.Plugin{
   367  						{Name: "CustomPlugin"},
   368  					},
   369  				},
   370  			},
   371  			defaultPlugins: &v1.Plugins{
   372  				MultiPoint: v1.PluginSet{
   373  					Enabled: []v1.Plugin{
   374  						{Name: "DefaultPlugin1"},
   375  						{Name: "DefaultPlugin2"},
   376  					},
   377  				},
   378  			},
   379  			expectedPlugins: &v1.Plugins{
   380  				MultiPoint: v1.PluginSet{
   381  					Enabled: []v1.Plugin{
   382  						{Name: "DefaultPlugin1"},
   383  						{Name: "DefaultPlugin2"},
   384  						{Name: "CustomPlugin"},
   385  					},
   386  				},
   387  			},
   388  		},
   389  		{
   390  			name: "Append disabled Multipoint plugins",
   391  			customPlugins: &v1.Plugins{
   392  				MultiPoint: v1.PluginSet{
   393  					Enabled: []v1.Plugin{
   394  						{Name: "CustomPlugin"},
   395  						{Name: "CustomPlugin2"},
   396  					},
   397  					Disabled: []v1.Plugin{
   398  						{Name: "DefaultPlugin2"},
   399  					},
   400  				},
   401  				Score: v1.PluginSet{
   402  					Disabled: []v1.Plugin{
   403  						{Name: "CustomPlugin2"},
   404  					},
   405  				},
   406  			},
   407  			defaultPlugins: &v1.Plugins{
   408  				MultiPoint: v1.PluginSet{
   409  					Enabled: []v1.Plugin{
   410  						{Name: "DefaultPlugin1"},
   411  						{Name: "DefaultPlugin2"},
   412  					},
   413  				},
   414  			},
   415  			expectedPlugins: &v1.Plugins{
   416  				MultiPoint: v1.PluginSet{
   417  					Enabled: []v1.Plugin{
   418  						{Name: "DefaultPlugin1"},
   419  						{Name: "CustomPlugin"},
   420  						{Name: "CustomPlugin2"},
   421  					},
   422  					Disabled: []v1.Plugin{
   423  						{Name: "DefaultPlugin2"},
   424  					},
   425  				},
   426  				Score: v1.PluginSet{
   427  					Disabled: []v1.Plugin{
   428  						{Name: "CustomPlugin2"},
   429  					},
   430  				},
   431  			},
   432  		},
   433  		{
   434  			name: "override default MultiPoint plugins with custom value",
   435  			customPlugins: &v1.Plugins{
   436  				MultiPoint: v1.PluginSet{
   437  					Enabled: []v1.Plugin{
   438  						{Name: "DefaultPlugin", Weight: ptr.To[int32](5)},
   439  					},
   440  				},
   441  			},
   442  			defaultPlugins: &v1.Plugins{
   443  				MultiPoint: v1.PluginSet{
   444  					Enabled: []v1.Plugin{
   445  						{Name: "DefaultPlugin"},
   446  					},
   447  				},
   448  			},
   449  			expectedPlugins: &v1.Plugins{
   450  				MultiPoint: v1.PluginSet{
   451  					Enabled: []v1.Plugin{
   452  						{Name: "DefaultPlugin", Weight: ptr.To[int32](5)},
   453  					},
   454  				},
   455  			},
   456  		},
   457  		{
   458  			name: "disabled MultiPoint plugin in default set",
   459  			defaultPlugins: &v1.Plugins{
   460  				MultiPoint: v1.PluginSet{
   461  					Enabled: []v1.Plugin{
   462  						{Name: "DefaultPlugin"},
   463  					},
   464  					Disabled: []v1.Plugin{
   465  						{Name: "DefaultPlugin2"},
   466  					},
   467  				},
   468  			},
   469  			customPlugins: &v1.Plugins{
   470  				MultiPoint: v1.PluginSet{
   471  					Enabled: []v1.Plugin{
   472  						{Name: "CustomPlugin"},
   473  					},
   474  				},
   475  			},
   476  			expectedPlugins: &v1.Plugins{
   477  				MultiPoint: v1.PluginSet{
   478  					Enabled: []v1.Plugin{
   479  						{Name: "DefaultPlugin"},
   480  						{Name: "CustomPlugin"},
   481  					},
   482  					Disabled: []v1.Plugin{
   483  						{Name: "DefaultPlugin2"},
   484  					},
   485  				},
   486  			},
   487  		},
   488  		{
   489  			name: "disabled MultiPoint plugin in default set for specific extension point",
   490  			defaultPlugins: &v1.Plugins{
   491  				MultiPoint: v1.PluginSet{
   492  					Enabled: []v1.Plugin{
   493  						{Name: "DefaultPlugin"},
   494  					},
   495  				},
   496  				Score: v1.PluginSet{
   497  					Disabled: []v1.Plugin{
   498  						{Name: "DefaultPlugin2"},
   499  					},
   500  				},
   501  			},
   502  			customPlugins: &v1.Plugins{
   503  				MultiPoint: v1.PluginSet{
   504  					Enabled: []v1.Plugin{
   505  						{Name: "CustomPlugin"},
   506  					},
   507  				},
   508  			},
   509  			expectedPlugins: &v1.Plugins{
   510  				MultiPoint: v1.PluginSet{
   511  					Enabled: []v1.Plugin{
   512  						{Name: "DefaultPlugin"},
   513  						{Name: "CustomPlugin"},
   514  					},
   515  				},
   516  				Score: v1.PluginSet{
   517  					Disabled: []v1.Plugin{
   518  						{Name: "DefaultPlugin2"},
   519  					},
   520  				},
   521  			},
   522  		},
   523  		{
   524  			name: "multipoint with only disabled gets merged",
   525  			defaultPlugins: &v1.Plugins{
   526  				MultiPoint: v1.PluginSet{
   527  					Enabled: []v1.Plugin{
   528  						{Name: "DefaultPlugin"},
   529  					},
   530  				},
   531  			},
   532  			customPlugins: &v1.Plugins{
   533  				MultiPoint: v1.PluginSet{
   534  					Disabled: []v1.Plugin{
   535  						{Name: "DefaultPlugin"},
   536  					},
   537  				},
   538  			},
   539  			expectedPlugins: &v1.Plugins{
   540  				MultiPoint: v1.PluginSet{
   541  					Disabled: []v1.Plugin{
   542  						{Name: "DefaultPlugin"},
   543  					},
   544  				},
   545  			},
   546  		},
   547  	}
   548  
   549  	for _, test := range tests {
   550  		t.Run(test.name, func(t *testing.T) {
   551  			logger, _ := ktesting.NewTestContext(t)
   552  			gotPlugins := mergePlugins(logger, test.defaultPlugins, test.customPlugins)
   553  			if d := cmp.Diff(test.expectedPlugins, gotPlugins); d != "" {
   554  				t.Fatalf("plugins mismatch (-want +got):\n%s", d)
   555  			}
   556  		})
   557  	}
   558  }