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 }