k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/scheduler/framework/plugins/podtopologyspread/scoring_test.go (about) 1 /* 2 Copyright 2019 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 "context" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 appsv1 "k8s.io/api/apps/v1" 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/labels" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/util/sets" 30 "k8s.io/client-go/informers" 31 "k8s.io/client-go/kubernetes/fake" 32 "k8s.io/klog/v2/ktesting" 33 "k8s.io/kubernetes/pkg/scheduler/apis/config" 34 "k8s.io/kubernetes/pkg/scheduler/framework" 35 "k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature" 36 plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing" 37 frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime" 38 "k8s.io/kubernetes/pkg/scheduler/internal/cache" 39 st "k8s.io/kubernetes/pkg/scheduler/testing" 40 tf "k8s.io/kubernetes/pkg/scheduler/testing/framework" 41 "k8s.io/utils/ptr" 42 ) 43 44 var podTopologySpreadFunc = frameworkruntime.FactoryAdapter(feature.Features{}, New) 45 46 // TestPreScoreSkip tests the cases that TopologySpread#PreScore returns the Skip status. 47 func TestPreScoreSkip(t *testing.T) { 48 tests := []struct { 49 name string 50 pod *v1.Pod 51 nodes []*v1.Node 52 objs []runtime.Object 53 config config.PodTopologySpreadArgs 54 }{ 55 { 56 name: "the pod doesn't have soft topology spread Constraints", 57 pod: st.MakePod().Name("p").Namespace("default").Obj(), 58 config: config.PodTopologySpreadArgs{ 59 DefaultingType: config.ListDefaulting, 60 }, 61 nodes: []*v1.Node{ 62 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 63 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 64 }, 65 }, 66 { 67 name: "default constraints and a replicaset that doesn't match", 68 pod: st.MakePod().Name("p").Namespace("default").Label("foo", "bar").Label("baz", "sup").OwnerReference("rs2", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")).Obj(), 69 config: config.PodTopologySpreadArgs{ 70 DefaultConstraints: []v1.TopologySpreadConstraint{ 71 { 72 MaxSkew: 2, 73 TopologyKey: "planet", 74 WhenUnsatisfiable: v1.ScheduleAnyway, 75 }, 76 }, 77 DefaultingType: config.ListDefaulting, 78 }, 79 nodes: []*v1.Node{ 80 st.MakeNode().Name("node-a").Label("planet", "mars").Obj(), 81 }, 82 objs: []runtime.Object{ 83 &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "rs1"}, Spec: appsv1.ReplicaSetSpec{Selector: st.MakeLabelSelector().Exists("tar").Obj()}}, 84 }, 85 }, 86 } 87 for _, tt := range tests { 88 t.Run(tt.name, func(t *testing.T) { 89 _, ctx := ktesting.NewTestContext(t) 90 ctx, cancel := context.WithCancel(ctx) 91 defer cancel() 92 informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0) 93 f, err := frameworkruntime.NewFramework(ctx, nil, nil, 94 frameworkruntime.WithSnapshotSharedLister(cache.NewSnapshot(nil, tt.nodes)), 95 frameworkruntime.WithInformerFactory(informerFactory)) 96 if err != nil { 97 t.Fatalf("Failed creating framework runtime: %v", err) 98 } 99 pl, err := New(ctx, &tt.config, f, feature.Features{}) 100 if err != nil { 101 t.Fatalf("Failed creating plugin: %v", err) 102 } 103 informerFactory.Start(ctx.Done()) 104 informerFactory.WaitForCacheSync(ctx.Done()) 105 p := pl.(*PodTopologySpread) 106 cs := framework.NewCycleState() 107 if s := p.PreScore(ctx, cs, tt.pod, tf.BuildNodeInfos(tt.nodes)); !s.IsSkip() { 108 t.Fatalf("Expected skip but got %v", s.AsError()) 109 } 110 }) 111 } 112 } 113 114 func TestPreScoreStateEmptyNodes(t *testing.T) { 115 tests := []struct { 116 name string 117 pod *v1.Pod 118 nodes []*v1.Node 119 objs []runtime.Object 120 config config.PodTopologySpreadArgs 121 want *preScoreState 122 enableNodeInclusionPolicy bool 123 }{ 124 { 125 name: "normal case", 126 pod: st.MakePod().Name("p").Label("foo", ""). 127 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 128 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 129 Obj(), 130 nodes: []*v1.Node{ 131 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 132 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 133 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 134 }, 135 config: config.PodTopologySpreadArgs{ 136 DefaultingType: config.ListDefaulting, 137 }, 138 want: &preScoreState{ 139 Constraints: []topologySpreadConstraint{ 140 { 141 MaxSkew: 1, 142 TopologyKey: "zone", 143 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 144 MinDomains: 1, 145 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 146 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 147 }, 148 { 149 MaxSkew: 1, 150 TopologyKey: v1.LabelHostname, 151 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 152 MinDomains: 1, 153 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 154 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 155 }, 156 }, 157 IgnoredNodes: sets.New[string](), 158 TopologyPairToPodCounts: map[topologyPair]*int64{ 159 {key: "zone", value: "zone1"}: ptr.To[int64](0), 160 {key: "zone", value: "zone2"}: ptr.To[int64](0), 161 }, 162 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2), topologyNormalizingWeight(3)}, 163 }, 164 }, 165 { 166 name: "null selector", 167 pod: st.MakePod().Name("p").Label("foo", ""). 168 SpreadConstraint(1, "zone", v1.ScheduleAnyway, nil, nil, nil, nil, nil). 169 Obj(), 170 nodes: []*v1.Node{ 171 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 172 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 173 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 174 }, 175 config: config.PodTopologySpreadArgs{ 176 DefaultingType: config.ListDefaulting, 177 }, 178 want: &preScoreState{ 179 Constraints: []topologySpreadConstraint{ 180 { 181 MaxSkew: 1, 182 TopologyKey: "zone", 183 Selector: labels.Nothing(), 184 MinDomains: 1, 185 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 186 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 187 }, 188 }, 189 IgnoredNodes: sets.New[string](), 190 TopologyPairToPodCounts: map[topologyPair]*int64{ 191 {key: "zone", value: "zone1"}: ptr.To[int64](0), 192 {key: "zone", value: "zone2"}: ptr.To[int64](0), 193 }, 194 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 195 }, 196 }, 197 { 198 name: "node-x doesn't have label zone", 199 pod: st.MakePod().Name("p").Label("foo", ""). 200 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 201 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 202 Obj(), 203 nodes: []*v1.Node{ 204 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 205 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 206 st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), 207 }, 208 config: config.PodTopologySpreadArgs{ 209 DefaultingType: config.ListDefaulting, 210 }, 211 want: &preScoreState{ 212 Constraints: []topologySpreadConstraint{ 213 { 214 MaxSkew: 1, 215 TopologyKey: "zone", 216 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 217 MinDomains: 1, 218 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 219 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 220 }, 221 { 222 MaxSkew: 1, 223 TopologyKey: v1.LabelHostname, 224 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 225 MinDomains: 1, 226 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 227 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 228 }, 229 }, 230 IgnoredNodes: sets.New("node-x"), 231 TopologyPairToPodCounts: map[topologyPair]*int64{ 232 {key: "zone", value: "zone1"}: ptr.To[int64](0), 233 }, 234 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1), topologyNormalizingWeight(2)}, 235 }, 236 }, 237 { 238 name: "system default constraints and a replicaset", 239 pod: st.MakePod().Name("p").Namespace("default").Label("foo", "tar").Label("baz", "sup").OwnerReference("rs1", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")).Obj(), 240 config: config.PodTopologySpreadArgs{ 241 DefaultingType: config.SystemDefaulting, 242 }, 243 nodes: []*v1.Node{ 244 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Label(v1.LabelTopologyZone, "mars").Obj(), 245 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Label(v1.LabelTopologyZone, "mars").Obj(), 246 // Nodes with no zone are not excluded. They are considered a separate zone. 247 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 248 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 249 }, 250 objs: []runtime.Object{ 251 &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "rs1"}, Spec: appsv1.ReplicaSetSpec{Selector: fooSelector}}, 252 }, 253 want: &preScoreState{ 254 Constraints: []topologySpreadConstraint{ 255 { 256 MaxSkew: 3, 257 TopologyKey: v1.LabelHostname, 258 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 259 MinDomains: 1, 260 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 261 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 262 }, 263 { 264 MaxSkew: 5, 265 TopologyKey: v1.LabelTopologyZone, 266 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 267 MinDomains: 1, 268 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 269 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 270 }, 271 }, 272 IgnoredNodes: sets.New[string](), 273 TopologyPairToPodCounts: map[topologyPair]*int64{ 274 {key: v1.LabelTopologyZone, value: "mars"}: ptr.To[int64](0), 275 {key: v1.LabelTopologyZone, value: ""}: ptr.To[int64](0), 276 }, 277 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(4), topologyNormalizingWeight(2)}, 278 }, 279 }, 280 { 281 name: "default constraints and a replicaset", 282 pod: st.MakePod().Name("p").Namespace("default").Label("foo", "tar").Label("baz", "sup").OwnerReference("rs1", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")).Obj(), 283 config: config.PodTopologySpreadArgs{ 284 DefaultConstraints: []v1.TopologySpreadConstraint{ 285 { 286 MaxSkew: 1, 287 TopologyKey: v1.LabelHostname, 288 WhenUnsatisfiable: v1.ScheduleAnyway, 289 }, 290 {MaxSkew: 2, 291 TopologyKey: "rack", 292 WhenUnsatisfiable: v1.DoNotSchedule, 293 }, 294 {MaxSkew: 2, TopologyKey: "planet", WhenUnsatisfiable: v1.ScheduleAnyway}, 295 }, 296 DefaultingType: config.ListDefaulting, 297 }, 298 nodes: []*v1.Node{ 299 st.MakeNode().Name("node-a").Label("rack", "rack1").Label(v1.LabelHostname, "node-a").Label("planet", "mars").Obj(), 300 }, 301 objs: []runtime.Object{ 302 &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "rs1"}, Spec: appsv1.ReplicaSetSpec{Selector: fooSelector}}, 303 }, 304 want: &preScoreState{ 305 Constraints: []topologySpreadConstraint{ 306 { 307 MaxSkew: 1, 308 TopologyKey: v1.LabelHostname, 309 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 310 MinDomains: 1, 311 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 312 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 313 }, 314 { 315 MaxSkew: 2, 316 TopologyKey: "planet", 317 Selector: mustConvertLabelSelectorAsSelector(t, fooSelector), 318 MinDomains: 1, 319 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 320 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 321 }, 322 }, 323 IgnoredNodes: sets.New[string](), 324 TopologyPairToPodCounts: map[topologyPair]*int64{ 325 {key: "planet", value: "mars"}: ptr.To[int64](0), 326 }, 327 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1), topologyNormalizingWeight(1)}, 328 }, 329 }, 330 { 331 name: "default constraints and a replicaset, but pod has constraints", 332 pod: st.MakePod().Name("p").Namespace("default").Label("foo", "bar").Label("baz", "sup"). 333 OwnerReference("rs1", appsv1.SchemeGroupVersion.WithKind("ReplicaSet")). 334 SpreadConstraint(1, "zone", v1.DoNotSchedule, barSelector, nil, nil, nil, nil). 335 SpreadConstraint(2, "planet", v1.ScheduleAnyway, st.MakeLabelSelector().Label("baz", "sup").Obj(), nil, nil, nil, nil). 336 Obj(), 337 config: config.PodTopologySpreadArgs{ 338 DefaultConstraints: []v1.TopologySpreadConstraint{ 339 { 340 MaxSkew: 2, 341 TopologyKey: "galaxy", 342 WhenUnsatisfiable: v1.ScheduleAnyway, 343 }, 344 }, 345 DefaultingType: config.ListDefaulting, 346 }, 347 nodes: []*v1.Node{ 348 st.MakeNode().Name("node-a").Label("planet", "mars").Label("galaxy", "andromeda").Obj(), 349 }, 350 objs: []runtime.Object{ 351 &appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "rs1"}, Spec: appsv1.ReplicaSetSpec{Selector: fooSelector}}, 352 }, 353 want: &preScoreState{ 354 Constraints: []topologySpreadConstraint{ 355 { 356 MaxSkew: 2, 357 TopologyKey: "planet", 358 Selector: mustConvertLabelSelectorAsSelector(t, st.MakeLabelSelector().Label("baz", "sup").Obj()), 359 MinDomains: 1, 360 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 361 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 362 }, 363 }, 364 IgnoredNodes: sets.New[string](), 365 TopologyPairToPodCounts: map[topologyPair]*int64{ 366 {"planet", "mars"}: ptr.To[int64](0), 367 }, 368 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(1)}, 369 }, 370 }, 371 { 372 name: "NodeAffinityPolicy honored with labelSelectors", 373 pod: st.MakePod().Name("p").Label("foo", ""). 374 NodeSelector(map[string]string{"foo": ""}). 375 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 376 Obj(), 377 nodes: []*v1.Node{ 378 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 379 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 380 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 381 }, 382 config: config.PodTopologySpreadArgs{ 383 DefaultingType: config.ListDefaulting, 384 }, 385 want: &preScoreState{ 386 Constraints: []topologySpreadConstraint{ 387 { 388 MaxSkew: 1, 389 TopologyKey: "zone", 390 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 391 MinDomains: 1, 392 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 393 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 394 }, 395 }, 396 IgnoredNodes: sets.New[string](), 397 TopologyPairToPodCounts: map[topologyPair]*int64{ 398 {key: "zone", value: "zone1"}: ptr.To[int64](0), 399 {key: "zone", value: "zone2"}: ptr.To[int64](0), 400 }, 401 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 402 }, 403 enableNodeInclusionPolicy: true, 404 }, 405 { 406 name: "NodeAffinityPolicy ignored with labelSelectors", 407 pod: st.MakePod().Name("p").Label("foo", ""). 408 NodeSelector(map[string]string{"foo": ""}). 409 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil). 410 Obj(), 411 nodes: []*v1.Node{ 412 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 413 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 414 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 415 }, 416 config: config.PodTopologySpreadArgs{ 417 DefaultingType: config.ListDefaulting, 418 }, 419 want: &preScoreState{ 420 Constraints: []topologySpreadConstraint{ 421 { 422 MaxSkew: 1, 423 TopologyKey: "zone", 424 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 425 MinDomains: 1, 426 NodeAffinityPolicy: v1.NodeInclusionPolicyIgnore, 427 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 428 }, 429 }, 430 IgnoredNodes: sets.New[string](), 431 TopologyPairToPodCounts: map[topologyPair]*int64{ 432 {key: "zone", value: "zone1"}: ptr.To[int64](0), 433 {key: "zone", value: "zone2"}: ptr.To[int64](0), 434 }, 435 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 436 }, 437 enableNodeInclusionPolicy: true, 438 }, 439 { 440 name: "NodeAffinityPolicy honored with nodeAffinity", 441 pod: st.MakePod().Name("p").Label("foo", ""). 442 NodeAffinityIn("foo", []string{""}). 443 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 444 Obj(), 445 nodes: []*v1.Node{ 446 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 447 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 448 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 449 }, 450 config: config.PodTopologySpreadArgs{ 451 DefaultingType: config.ListDefaulting, 452 }, 453 want: &preScoreState{ 454 Constraints: []topologySpreadConstraint{ 455 { 456 MaxSkew: 1, 457 TopologyKey: "zone", 458 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 459 MinDomains: 1, 460 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 461 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 462 }, 463 }, 464 IgnoredNodes: sets.New[string](), 465 TopologyPairToPodCounts: map[topologyPair]*int64{ 466 {key: "zone", value: "zone1"}: ptr.To[int64](0), 467 {key: "zone", value: "zone2"}: ptr.To[int64](0), 468 }, 469 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 470 }, 471 enableNodeInclusionPolicy: true, 472 }, 473 { 474 name: "NodeAffinityPolicy ignored with nodeAffinity", 475 pod: st.MakePod().Name("p").Label("foo", ""). 476 NodeAffinityIn("foo", []string{""}). 477 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, &ignorePolicy, nil, nil). 478 Obj(), 479 nodes: []*v1.Node{ 480 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 481 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 482 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 483 }, 484 config: config.PodTopologySpreadArgs{ 485 DefaultingType: config.ListDefaulting, 486 }, 487 want: &preScoreState{ 488 Constraints: []topologySpreadConstraint{ 489 { 490 MaxSkew: 1, 491 TopologyKey: "zone", 492 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 493 MinDomains: 1, 494 NodeAffinityPolicy: v1.NodeInclusionPolicyIgnore, 495 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 496 }, 497 }, 498 IgnoredNodes: sets.New[string](), 499 TopologyPairToPodCounts: map[topologyPair]*int64{ 500 {key: "zone", value: "zone1"}: ptr.To[int64](0), 501 {key: "zone", value: "zone2"}: ptr.To[int64](0), 502 }, 503 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 504 }, 505 enableNodeInclusionPolicy: true, 506 }, 507 { 508 name: "NodeTaintsPolicy honored", 509 pod: st.MakePod().Name("p").Label("foo", ""). 510 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, &honorPolicy, nil). 511 Obj(), 512 nodes: []*v1.Node{ 513 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 514 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 515 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Taints(taints).Obj(), 516 }, 517 config: config.PodTopologySpreadArgs{ 518 DefaultingType: config.ListDefaulting, 519 }, 520 want: &preScoreState{ 521 Constraints: []topologySpreadConstraint{ 522 { 523 MaxSkew: 1, 524 TopologyKey: "zone", 525 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 526 MinDomains: 1, 527 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 528 NodeTaintsPolicy: v1.NodeInclusionPolicyHonor, 529 }, 530 }, 531 IgnoredNodes: sets.New[string](), 532 TopologyPairToPodCounts: map[topologyPair]*int64{ 533 {key: "zone", value: "zone1"}: ptr.To[int64](0), 534 {key: "zone", value: "zone2"}: ptr.To[int64](0), 535 }, 536 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 537 }, 538 enableNodeInclusionPolicy: true, 539 }, 540 { 541 name: "NodeTaintsPolicy ignored", 542 pod: st.MakePod().Name("p").Label("foo", ""). 543 SpreadConstraint(1, "zone", v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 544 Obj(), 545 nodes: []*v1.Node{ 546 st.MakeNode().Name("node-a").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-a").Obj(), 547 st.MakeNode().Name("node-b").Label("zone", "zone1").Label("foo", "").Label(v1.LabelHostname, "node-b").Obj(), 548 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Taints(taints).Obj(), 549 }, 550 config: config.PodTopologySpreadArgs{ 551 DefaultingType: config.ListDefaulting, 552 }, 553 want: &preScoreState{ 554 Constraints: []topologySpreadConstraint{ 555 { 556 MaxSkew: 1, 557 TopologyKey: "zone", 558 Selector: mustConvertLabelSelectorAsSelector(t, barSelector), 559 MinDomains: 1, 560 NodeAffinityPolicy: v1.NodeInclusionPolicyHonor, 561 NodeTaintsPolicy: v1.NodeInclusionPolicyIgnore, 562 }, 563 }, 564 IgnoredNodes: sets.New[string](), 565 TopologyPairToPodCounts: map[topologyPair]*int64{ 566 {key: "zone", value: "zone1"}: ptr.To[int64](0), 567 {key: "zone", value: "zone2"}: ptr.To[int64](0), 568 }, 569 TopologyNormalizingWeight: []float64{topologyNormalizingWeight(2)}, 570 }, 571 enableNodeInclusionPolicy: true, 572 }, 573 } 574 for _, tt := range tests { 575 t.Run(tt.name, func(t *testing.T) { 576 _, ctx := ktesting.NewTestContext(t) 577 ctx, cancel := context.WithCancel(ctx) 578 defer cancel() 579 informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0) 580 f, err := frameworkruntime.NewFramework(ctx, nil, nil, 581 frameworkruntime.WithSnapshotSharedLister(cache.NewSnapshot(nil, tt.nodes)), 582 frameworkruntime.WithInformerFactory(informerFactory)) 583 if err != nil { 584 t.Fatalf("Failed creating framework runtime: %v", err) 585 } 586 pl, err := New(ctx, &tt.config, f, feature.Features{EnableNodeInclusionPolicyInPodTopologySpread: tt.enableNodeInclusionPolicy}) 587 if err != nil { 588 t.Fatalf("Failed creating plugin: %v", err) 589 } 590 informerFactory.Start(ctx.Done()) 591 informerFactory.WaitForCacheSync(ctx.Done()) 592 p := pl.(*PodTopologySpread) 593 cs := framework.NewCycleState() 594 if s := p.PreScore(ctx, cs, tt.pod, tf.BuildNodeInfos(tt.nodes)); !s.IsSuccess() { 595 t.Fatal(s.AsError()) 596 } 597 598 got, err := getPreScoreState(cs) 599 if err != nil { 600 t.Fatal(err) 601 } 602 if diff := cmp.Diff(tt.want, got, cmpOpts...); diff != "" { 603 t.Errorf("PodTopologySpread#PreScore() returned (-want, +got):\n%s", diff) 604 } 605 }) 606 } 607 } 608 609 func TestPodTopologySpreadScore(t *testing.T) { 610 tests := []struct { 611 name string 612 pod *v1.Pod 613 existingPods []*v1.Pod 614 nodes []*v1.Node 615 failedNodes []*v1.Node // nodes + failedNodes = all nodes 616 objs []runtime.Object 617 want framework.NodeScoreList 618 enableNodeInclusionPolicy bool 619 enableMatchLabelKeys bool 620 }{ 621 // Explanation on the Legend: 622 // a) X/Y means there are X matching pods on node1 and Y on node2, both nodes are candidates 623 // (i.e. they have passed all predicates) 624 // b) X/~Y~ means there are X matching pods on node1 and Y on node2, but node Y is NOT a candidate 625 // c) X/?Y? means there are X matching pods on node1 and Y on node2, both nodes are candidates 626 // but node2 doesn't have all required topologyKeys present. 627 { 628 name: "one constraint on node, no existing pods", 629 pod: st.MakePod().Name("p").Label("foo", ""). 630 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 631 Obj(), 632 nodes: []*v1.Node{ 633 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 634 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 635 }, 636 want: []framework.NodeScore{ 637 {Name: "node-a", Score: 100}, 638 {Name: "node-b", Score: 100}, 639 }, 640 }, 641 { 642 // if there is only one candidate node, it should be scored to 100 643 name: "one constraint on node, only one node is candidate", 644 pod: st.MakePod().Name("p").Label("foo", ""). 645 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 646 Obj(), 647 existingPods: []*v1.Pod{ 648 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 649 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 650 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 651 }, 652 nodes: []*v1.Node{ 653 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 654 }, 655 failedNodes: []*v1.Node{ 656 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 657 }, 658 want: []framework.NodeScore{ 659 {Name: "node-a", Score: 100}, 660 }, 661 }, 662 { 663 name: "one constraint on node, all nodes have the same number of matching pods", 664 pod: st.MakePod().Name("p").Label("foo", ""). 665 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 666 Obj(), 667 existingPods: []*v1.Pod{ 668 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 669 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 670 }, 671 nodes: []*v1.Node{ 672 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 673 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 674 }, 675 want: []framework.NodeScore{ 676 {Name: "node-a", Score: 100}, 677 {Name: "node-b", Score: 100}, 678 }, 679 }, 680 { 681 // matching pods spread as 2/1/0/3. 682 name: "one constraint on node, all 4 nodes are candidates", 683 pod: st.MakePod().Name("p").Label("foo", ""). 684 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 685 Obj(), 686 existingPods: []*v1.Pod{ 687 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 688 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 689 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 690 st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), 691 st.MakePod().Name("p-d2").Node("node-d").Label("foo", "").Obj(), 692 st.MakePod().Name("p-d3").Node("node-d").Label("foo", "").Obj(), 693 }, 694 nodes: []*v1.Node{ 695 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 696 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 697 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 698 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 699 }, 700 failedNodes: []*v1.Node{}, 701 want: []framework.NodeScore{ 702 {Name: "node-a", Score: 20}, 703 {Name: "node-b", Score: 60}, 704 {Name: "node-c", Score: 100}, 705 {Name: "node-d", Score: 0}, 706 }, 707 }, 708 { 709 name: "one constraint on node, null selector", 710 pod: st.MakePod().Name("p").Label("foo", ""). 711 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, nil, nil, nil, nil, nil). 712 Obj(), 713 existingPods: []*v1.Pod{ 714 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 715 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 716 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 717 st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), 718 st.MakePod().Name("p-d2").Node("node-d").Label("foo", "").Obj(), 719 st.MakePod().Name("p-d3").Node("node-d").Label("foo", "").Obj(), 720 }, 721 nodes: []*v1.Node{ 722 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 723 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 724 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 725 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 726 }, 727 want: []framework.NodeScore{ 728 {Name: "node-a", Score: 100}, 729 {Name: "node-b", Score: 100}, 730 {Name: "node-c", Score: 100}, 731 {Name: "node-d", Score: 100}, 732 }, 733 }, 734 { 735 name: "one constraint on node, all 4 nodes are candidates, maxSkew=2", 736 pod: st.MakePod().Name("p").Label("foo", ""). 737 SpreadConstraint(2, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 738 Obj(), 739 // matching pods spread as 2/1/0/3. 740 existingPods: []*v1.Pod{ 741 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 742 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 743 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 744 st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), 745 st.MakePod().Name("p-d2").Node("node-d").Label("foo", "").Obj(), 746 st.MakePod().Name("p-d3").Node("node-d").Label("foo", "").Obj(), 747 }, 748 nodes: []*v1.Node{ 749 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 750 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 751 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 752 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 753 }, 754 failedNodes: []*v1.Node{}, 755 want: []framework.NodeScore{ 756 {Name: "node-a", Score: 33}, // +13, compared to maxSkew=1 757 {Name: "node-b", Score: 66}, // +6, compared to maxSkew=1 758 {Name: "node-c", Score: 100}, 759 {Name: "node-d", Score: 16}, // +16, compared to maxSkew=1 760 }, 761 }, 762 { 763 name: "one constraint on node, all 4 nodes are candidates, maxSkew=3", 764 pod: st.MakePod().Name("p").Label("foo", ""). 765 SpreadConstraint(3, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 766 Obj(), 767 existingPods: []*v1.Pod{ 768 // matching pods spread as 4/3/2/1. 769 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 770 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 771 st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), 772 st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), 773 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 774 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 775 st.MakePod().Name("p-b3").Node("node-b").Label("foo", "").Obj(), 776 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 777 st.MakePod().Name("p-c2").Node("node-c").Label("foo", "").Obj(), 778 st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), 779 }, 780 nodes: []*v1.Node{ 781 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 782 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 783 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 784 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 785 }, 786 failedNodes: []*v1.Node{}, 787 want: []framework.NodeScore{ 788 {Name: "node-a", Score: 44}, // +16 compared to maxSkew=1 789 {Name: "node-b", Score: 66}, // +9 compared to maxSkew=1 790 {Name: "node-c", Score: 77}, // +6 compared to maxSkew=1 791 {Name: "node-d", Score: 100}, 792 }, 793 }, 794 { 795 name: "system defaulting, nodes don't have zone, pods match service", 796 pod: st.MakePod().Name("p").Label("foo", "").Obj(), 797 existingPods: []*v1.Pod{ 798 // matching pods spread as 4/3/2/1. 799 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 800 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 801 st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), 802 st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), 803 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 804 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 805 st.MakePod().Name("p-b3").Node("node-b").Label("foo", "").Obj(), 806 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 807 st.MakePod().Name("p-c2").Node("node-c").Label("foo", "").Obj(), 808 st.MakePod().Name("p-d1").Node("node-d").Label("foo", "").Obj(), 809 }, 810 nodes: []*v1.Node{ 811 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 812 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 813 st.MakeNode().Name("node-c").Label(v1.LabelHostname, "node-c").Obj(), 814 st.MakeNode().Name("node-d").Label(v1.LabelHostname, "node-d").Obj(), 815 }, 816 failedNodes: []*v1.Node{}, 817 objs: []runtime.Object{ 818 &v1.Service{Spec: v1.ServiceSpec{Selector: map[string]string{"foo": ""}}}, 819 }, 820 want: []framework.NodeScore{ 821 // Same scores as if we were using one spreading constraint. 822 {Name: "node-a", Score: 44}, 823 {Name: "node-b", Score: 66}, 824 {Name: "node-c", Score: 77}, 825 {Name: "node-d", Score: 100}, 826 }, 827 }, 828 { 829 // matching pods spread as 4/2/1/~3~ (node4 is not a candidate) 830 name: "one constraint on node, 3 out of 4 nodes are candidates", 831 pod: st.MakePod().Name("p").Label("foo", ""). 832 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 833 Obj(), 834 existingPods: []*v1.Pod{ 835 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 836 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 837 st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), 838 st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), 839 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 840 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 841 st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(), 842 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 843 st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(), 844 st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), 845 }, 846 nodes: []*v1.Node{ 847 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 848 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 849 st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), 850 }, 851 failedNodes: []*v1.Node{ 852 st.MakeNode().Name("node-y").Label(v1.LabelHostname, "node-y").Obj(), 853 }, 854 want: []framework.NodeScore{ 855 {Name: "node-a", Score: 33}, 856 {Name: "node-b", Score: 83}, 857 {Name: "node-x", Score: 100}, 858 }, 859 }, 860 { 861 // matching pods spread as 4/?2?/1/~3~, total = 4+?+1 = 5 (as node2 is problematic) 862 name: "one constraint on node, 3 out of 4 nodes are candidates, one node doesn't match topology key", 863 pod: st.MakePod().Name("p").Label("foo", ""). 864 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 865 Obj(), 866 existingPods: []*v1.Pod{ 867 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 868 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 869 st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), 870 st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), 871 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 872 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 873 st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(), 874 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 875 st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(), 876 st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), 877 }, 878 nodes: []*v1.Node{ 879 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 880 st.MakeNode().Name("node-b").Label("n", "node-b").Obj(), // label `n` doesn't match topologyKey 881 st.MakeNode().Name("node-x").Label(v1.LabelHostname, "node-x").Obj(), 882 }, 883 failedNodes: []*v1.Node{ 884 st.MakeNode().Name("node-y").Label(v1.LabelHostname, "node-y").Obj(), 885 }, 886 want: []framework.NodeScore{ 887 {Name: "node-a", Score: 16}, 888 {Name: "node-b", Score: 0}, 889 {Name: "node-x", Score: 100}, 890 }, 891 }, 892 { 893 // matching pods spread as 4/2/1/~3~ 894 name: "one constraint on zone, 3 out of 4 nodes are candidates", 895 pod: st.MakePod().Name("p").Label("foo", ""). 896 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 897 Obj(), 898 existingPods: []*v1.Pod{ 899 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 900 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 901 st.MakePod().Name("p-a3").Node("node-a").Label("foo", "").Obj(), 902 st.MakePod().Name("p-a4").Node("node-a").Label("foo", "").Obj(), 903 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 904 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 905 st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(), 906 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 907 st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(), 908 st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), 909 }, 910 nodes: []*v1.Node{ 911 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 912 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 913 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 914 }, 915 failedNodes: []*v1.Node{ 916 st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), 917 }, 918 want: []framework.NodeScore{ 919 {Name: "node-a", Score: 75}, 920 {Name: "node-b", Score: 75}, 921 {Name: "node-x", Score: 100}, 922 }, 923 }, 924 { 925 // matching pods spread as 2/~1~/2/~4~. 926 name: "two Constraints on zone and node, 2 out of 4 nodes are candidates", 927 pod: st.MakePod().Name("p").Label("foo", ""). 928 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 929 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 930 Obj(), 931 existingPods: []*v1.Pod{ 932 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 933 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 934 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 935 st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(), 936 st.MakePod().Name("p-x2").Node("node-x").Label("foo", "").Obj(), 937 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 938 st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(), 939 st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(), 940 st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(), 941 }, 942 nodes: []*v1.Node{ 943 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 944 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 945 }, 946 failedNodes: []*v1.Node{ 947 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 948 st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), 949 }, 950 want: []framework.NodeScore{ 951 {Name: "node-a", Score: 100}, 952 {Name: "node-x", Score: 63}, 953 }, 954 }, 955 { 956 // If Constraints hold different labelSelectors, it's a little complex. 957 // +----------------------+------------------------+ 958 // | zone1 | zone2 | 959 // +----------------------+------------------------+ 960 // | node-a | node-b | node-x | node-y | 961 // +--------+-------------+--------+---------------+ 962 // | P{foo} | P{foo, bar} | | P{foo} P{bar} | 963 // +--------+-------------+--------+---------------+ 964 // For the first constraint (zone): the matching pods spread as 2/2/1/1 965 // For the second constraint (node): the matching pods spread as 0/1/0/1 966 name: "two Constraints on zone and node, with different labelSelectors", 967 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). 968 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 969 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 970 Obj(), 971 existingPods: []*v1.Pod{ 972 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 973 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(), 974 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 975 st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(), 976 }, 977 nodes: []*v1.Node{ 978 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 979 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 980 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 981 st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), 982 }, 983 failedNodes: []*v1.Node{}, 984 want: []framework.NodeScore{ 985 {Name: "node-a", Score: 60}, 986 {Name: "node-b", Score: 20}, 987 {Name: "node-x", Score: 100}, 988 {Name: "node-y", Score: 60}, 989 }, 990 }, 991 { 992 // For the first constraint (zone): the matching pods spread as 0/0/2/2 993 // For the second constraint (node): the matching pods spread as 0/1/0/1 994 name: "two Constraints on zone and node, with different labelSelectors, some nodes have 0 pods", 995 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). 996 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 997 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 998 Obj(), 999 existingPods: []*v1.Pod{ 1000 st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(), 1001 st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(), 1002 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Label("bar", "").Obj(), 1003 }, 1004 nodes: []*v1.Node{ 1005 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 1006 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 1007 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 1008 st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), 1009 }, 1010 failedNodes: []*v1.Node{}, 1011 want: []framework.NodeScore{ 1012 {Name: "node-a", Score: 100}, 1013 {Name: "node-b", Score: 60}, 1014 {Name: "node-x", Score: 40}, 1015 {Name: "node-y", Score: 0}, 1016 }, 1017 }, 1018 { 1019 // For the first constraint (zone): the matching pods spread as 2/2/1/~1~ 1020 // For the second constraint (node): the matching pods spread as 0/1/0/~1~ 1021 name: "two Constraints on zone and node, with different labelSelectors, 3 out of 4 nodes are candidates", 1022 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). 1023 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1024 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 1025 Obj(), 1026 existingPods: []*v1.Pod{ 1027 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1028 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(), 1029 st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(), 1030 st.MakePod().Name("p-y2").Node("node-y").Label("bar", "").Obj(), 1031 }, 1032 nodes: []*v1.Node{ 1033 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 1034 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 1035 st.MakeNode().Name("node-x").Label("zone", "zone2").Label(v1.LabelHostname, "node-x").Obj(), 1036 }, 1037 failedNodes: []*v1.Node{ 1038 st.MakeNode().Name("node-y").Label("zone", "zone2").Label(v1.LabelHostname, "node-y").Obj(), 1039 }, 1040 want: []framework.NodeScore{ 1041 {Name: "node-a", Score: 50}, 1042 {Name: "node-b", Score: 25}, 1043 {Name: "node-x", Score: 100}, 1044 }, 1045 }, 1046 { 1047 name: "existing pods in a different namespace do not count", 1048 pod: st.MakePod().Name("p").Label("foo", ""). 1049 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1050 Obj(), 1051 existingPods: []*v1.Pod{ 1052 st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(), 1053 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1054 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1055 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 1056 }, 1057 nodes: []*v1.Node{ 1058 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 1059 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 1060 }, 1061 want: []framework.NodeScore{ 1062 {Name: "node-a", Score: 100}, 1063 {Name: "node-b", Score: 33}, 1064 }, 1065 }, 1066 { 1067 name: "terminating Pods should be excluded", 1068 pod: st.MakePod().Name("p").Label("foo", ""). 1069 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1070 Obj(), 1071 nodes: []*v1.Node{ 1072 st.MakeNode().Name("node-a").Label(v1.LabelHostname, "node-a").Obj(), 1073 st.MakeNode().Name("node-b").Label(v1.LabelHostname, "node-b").Obj(), 1074 }, 1075 existingPods: []*v1.Pod{ 1076 st.MakePod().Name("p-a").Node("node-a").Label("foo", "").Terminating().Obj(), 1077 st.MakePod().Name("p-b").Node("node-b").Label("foo", "").Obj(), 1078 }, 1079 want: []framework.NodeScore{ 1080 {Name: "node-a", Score: 100}, 1081 {Name: "node-b", Score: 0}, 1082 }, 1083 }, 1084 { 1085 // This test is artificial. In the real world, API Server would fail a pod's creation 1086 // when non-default minDomains is specified along with SchedulingAnyway. 1087 name: "minDomains has no effect when ScheduleAnyway", 1088 pod: st.MakePod().Name("p").Label("foo", "").SpreadConstraint( 1089 2, 1090 "node", 1091 v1.ScheduleAnyway, 1092 fooSelector, 1093 ptr.To[int32](10), // larger than the number of domains(3) 1094 nil, 1095 nil, 1096 nil, 1097 ).Obj(), 1098 nodes: []*v1.Node{ 1099 st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), 1100 st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), 1101 st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), 1102 }, 1103 existingPods: []*v1.Pod{ 1104 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1105 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1106 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1107 st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(), 1108 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1109 }, 1110 want: []framework.NodeScore{ 1111 {Name: "node-a", Score: 75}, 1112 {Name: "node-b", Score: 75}, 1113 {Name: "node-c", Score: 100}, 1114 }, 1115 }, 1116 { 1117 name: "NodeAffinityPolicy honoed with labelSelectors", 1118 pod: st.MakePod().Name("p").Label("foo", ""). 1119 NodeSelector(map[string]string{"foo": ""}). 1120 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1121 Obj(), 1122 nodes: []*v1.Node{ 1123 st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), 1124 st.MakeNode().Name("node-b").Label("node", "node-b").Label("foo", "").Obj(), 1125 st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), 1126 }, 1127 existingPods: []*v1.Pod{ 1128 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1129 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1130 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1131 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1132 }, 1133 want: []framework.NodeScore{ 1134 {Name: "node-a", Score: 0}, 1135 {Name: "node-b", Score: 33}, 1136 {Name: "node-c", Score: 100}, 1137 }, 1138 enableNodeInclusionPolicy: true, 1139 }, 1140 { 1141 name: "NodeAffinityPolicy ignored with labelSelectors", 1142 pod: st.MakePod().Name("p").Label("foo", ""). 1143 NodeSelector(map[string]string{"foo": ""}). 1144 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil). 1145 Obj(), 1146 nodes: []*v1.Node{ 1147 st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), 1148 st.MakeNode().Name("node-b").Label("node", "node-b").Label("foo", "").Obj(), 1149 st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), 1150 }, 1151 existingPods: []*v1.Pod{ 1152 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1153 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1154 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1155 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1156 }, 1157 want: []framework.NodeScore{ 1158 {Name: "node-a", Score: 66}, 1159 {Name: "node-b", Score: 100}, 1160 {Name: "node-c", Score: 100}, 1161 }, 1162 enableNodeInclusionPolicy: true, 1163 }, 1164 { 1165 name: "NodeAffinityPolicy honoed with nodeAffinity", 1166 pod: st.MakePod().Name("p").Label("foo", ""). 1167 NodeAffinityIn("foo", []string{""}). 1168 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1169 Obj(), 1170 nodes: []*v1.Node{ 1171 st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), 1172 st.MakeNode().Name("node-b").Label("node", "node-b").Label("foo", "").Obj(), 1173 st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), 1174 }, 1175 existingPods: []*v1.Pod{ 1176 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1177 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1178 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1179 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1180 }, 1181 want: []framework.NodeScore{ 1182 {Name: "node-a", Score: 0}, 1183 {Name: "node-b", Score: 33}, 1184 {Name: "node-c", Score: 100}, 1185 }, 1186 enableNodeInclusionPolicy: true, 1187 }, 1188 { 1189 name: "NodeAffinityPolicy ignored with nodeAffinity", 1190 pod: st.MakePod().Name("p").Label("foo", ""). 1191 NodeAffinityIn("foo", []string{""}). 1192 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, &ignorePolicy, nil, nil). 1193 Obj(), 1194 nodes: []*v1.Node{ 1195 st.MakeNode().Name("node-a").Label("node", "node-a").Label("foo", "").Obj(), 1196 st.MakeNode().Name("node-b").Label("node", "node-b").Label("foo", "").Obj(), 1197 st.MakeNode().Name("node-c").Label("node", "node-c").Obj(), 1198 }, 1199 existingPods: []*v1.Pod{ 1200 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1201 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1202 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1203 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1204 }, 1205 want: []framework.NodeScore{ 1206 {Name: "node-a", Score: 66}, 1207 {Name: "node-b", Score: 100}, 1208 {Name: "node-c", Score: 100}, 1209 }, 1210 enableNodeInclusionPolicy: true, 1211 }, 1212 { 1213 name: "NodeTaintsPolicy honored", 1214 pod: st.MakePod().Name("p").Label("foo", ""). 1215 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, &honorPolicy, nil). 1216 Obj(), 1217 nodes: []*v1.Node{ 1218 st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), 1219 st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), 1220 st.MakeNode().Name("node-c").Label("node", "node-c").Taints(taints).Obj(), 1221 }, 1222 existingPods: []*v1.Pod{ 1223 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1224 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1225 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1226 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1227 }, 1228 want: []framework.NodeScore{ 1229 {Name: "node-a", Score: 0}, 1230 {Name: "node-b", Score: 33}, 1231 {Name: "node-c", Score: 100}, 1232 }, 1233 enableNodeInclusionPolicy: true, 1234 }, 1235 { 1236 name: "NodeTaintsPolicy ignored", 1237 pod: st.MakePod().Name("p").Label("foo", ""). 1238 SpreadConstraint(1, "node", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1239 Obj(), 1240 nodes: []*v1.Node{ 1241 st.MakeNode().Name("node-a").Label("node", "node-a").Obj(), 1242 st.MakeNode().Name("node-b").Label("node", "node-b").Obj(), 1243 st.MakeNode().Name("node-c").Label("node", "node-c").Taints(taints).Obj(), 1244 }, 1245 existingPods: []*v1.Pod{ 1246 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1247 st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(), 1248 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(), 1249 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1250 }, 1251 want: []framework.NodeScore{ 1252 {Name: "node-a", Score: 66}, 1253 {Name: "node-b", Score: 100}, 1254 {Name: "node-c", Score: 100}, 1255 }, 1256 enableNodeInclusionPolicy: true, 1257 }, 1258 { 1259 name: "matchLabelKeys ignored when feature gate disabled", 1260 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", ""). 1261 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, []string{"baz"}). 1262 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}). 1263 Obj(), 1264 existingPods: []*v1.Pod{ 1265 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1266 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(), 1267 st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(), 1268 st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(), 1269 }, 1270 nodes: []*v1.Node{ 1271 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 1272 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 1273 st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), 1274 st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), 1275 }, 1276 want: []framework.NodeScore{ 1277 {Name: "node-a", Score: 60}, 1278 {Name: "node-b", Score: 20}, 1279 {Name: "node-c", Score: 60}, 1280 {Name: "node-d", Score: 100}, 1281 }, 1282 enableMatchLabelKeys: false, 1283 }, 1284 { 1285 name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty", 1286 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). 1287 SpreadConstraint(1, "zone", v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}). 1288 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"bar"}). 1289 Obj(), 1290 existingPods: []*v1.Pod{ 1291 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1292 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(), 1293 st.MakePod().Name("p-y1").Node("node-c").Label("foo", "").Obj(), 1294 st.MakePod().Name("p-y2").Node("node-c").Label("bar", "").Obj(), 1295 }, 1296 nodes: []*v1.Node{ 1297 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 1298 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 1299 st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), 1300 st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), 1301 }, 1302 want: []framework.NodeScore{ 1303 {Name: "node-a", Score: 60}, 1304 {Name: "node-b", Score: 20}, 1305 {Name: "node-c", Score: 60}, 1306 {Name: "node-d", Score: 100}, 1307 }, 1308 enableMatchLabelKeys: true, 1309 }, 1310 { 1311 name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty", 1312 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Label("baz", ""). 1313 SpreadConstraint(1, "zone", v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1314 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, []string{"baz"}). 1315 Obj(), 1316 existingPods: []*v1.Pod{ 1317 st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(), 1318 st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Label("baz", "").Obj(), 1319 st.MakePod().Name("p-c1").Node("node-c").Label("foo", "").Obj(), 1320 st.MakePod().Name("p-c2").Node("node-c").Label("bar", "").Obj(), 1321 st.MakePod().Name("p-d3").Node("node-c").Label("bar", "").Label("baz", "").Obj(), 1322 }, 1323 nodes: []*v1.Node{ 1324 st.MakeNode().Name("node-a").Label("zone", "zone1").Label(v1.LabelHostname, "node-a").Obj(), 1325 st.MakeNode().Name("node-b").Label("zone", "zone1").Label(v1.LabelHostname, "node-b").Obj(), 1326 st.MakeNode().Name("node-c").Label("zone", "zone2").Label(v1.LabelHostname, "node-c").Obj(), 1327 st.MakeNode().Name("node-d").Label("zone", "zone2").Label(v1.LabelHostname, "node-d").Obj(), 1328 }, 1329 want: []framework.NodeScore{ 1330 {Name: "node-a", Score: 60}, 1331 {Name: "node-b", Score: 20}, 1332 {Name: "node-c", Score: 60}, 1333 {Name: "node-d", Score: 100}, 1334 }, 1335 enableMatchLabelKeys: true, 1336 }, 1337 } 1338 for _, tt := range tests { 1339 t.Run(tt.name, func(t *testing.T) { 1340 _, ctx := ktesting.NewTestContext(t) 1341 ctx, cancel := context.WithCancel(ctx) 1342 t.Cleanup(cancel) 1343 allNodes := append([]*v1.Node{}, tt.nodes...) 1344 allNodes = append(allNodes, tt.failedNodes...) 1345 state := framework.NewCycleState() 1346 pl := plugintesting.SetupPluginWithInformers(ctx, t, podTopologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.SystemDefaulting}, cache.NewSnapshot(tt.existingPods, allNodes), tt.objs) 1347 p := pl.(*PodTopologySpread) 1348 p.enableNodeInclusionPolicyInPodTopologySpread = tt.enableNodeInclusionPolicy 1349 p.enableMatchLabelKeysInPodTopologySpread = tt.enableMatchLabelKeys 1350 1351 status := p.PreScore(ctx, state, tt.pod, tf.BuildNodeInfos(tt.nodes)) 1352 if !status.IsSuccess() { 1353 t.Errorf("unexpected error: %v", status) 1354 } 1355 1356 var gotList framework.NodeScoreList 1357 for _, n := range tt.nodes { 1358 nodeName := n.Name 1359 score, status := p.Score(ctx, state, tt.pod, nodeName) 1360 if !status.IsSuccess() { 1361 t.Errorf("unexpected error: %v", status) 1362 } 1363 gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score}) 1364 } 1365 1366 status = p.NormalizeScore(ctx, state, tt.pod, gotList) 1367 if !status.IsSuccess() { 1368 t.Errorf("unexpected error: %v", status) 1369 } 1370 if diff := cmp.Diff(tt.want, gotList, cmpOpts...); diff != "" { 1371 t.Errorf("unexpected scores (-want,+got):\n%s", diff) 1372 } 1373 }) 1374 } 1375 } 1376 1377 func BenchmarkTestPodTopologySpreadScore(b *testing.B) { 1378 tests := []struct { 1379 name string 1380 pod *v1.Pod 1381 existingPodsNum int 1382 allNodesNum int 1383 filteredNodesNum int 1384 }{ 1385 { 1386 name: "1000nodes/single-constraint-zone", 1387 pod: st.MakePod().Name("p").Label("foo", ""). 1388 SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1389 Obj(), 1390 existingPodsNum: 10000, 1391 allNodesNum: 1000, 1392 filteredNodesNum: 500, 1393 }, 1394 { 1395 name: "1000nodes/single-constraint-node", 1396 pod: st.MakePod().Name("p").Label("foo", ""). 1397 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1398 Obj(), 1399 existingPodsNum: 10000, 1400 allNodesNum: 1000, 1401 filteredNodesNum: 500, 1402 }, 1403 { 1404 name: "1000nodes/two-Constraints-zone-node", 1405 pod: st.MakePod().Name("p").Label("foo", "").Label("bar", ""). 1406 SpreadConstraint(1, v1.LabelTopologyZone, v1.ScheduleAnyway, fooSelector, nil, nil, nil, nil). 1407 SpreadConstraint(1, v1.LabelHostname, v1.ScheduleAnyway, barSelector, nil, nil, nil, nil). 1408 Obj(), 1409 existingPodsNum: 10000, 1410 allNodesNum: 1000, 1411 filteredNodesNum: 500, 1412 }, 1413 } 1414 for _, tt := range tests { 1415 b.Run(tt.name, func(b *testing.B) { 1416 _, ctx := ktesting.NewTestContext(b) 1417 existingPods, allNodes, filteredNodes := st.MakeNodesAndPodsForEvenPodsSpread(tt.pod.Labels, tt.existingPodsNum, tt.allNodesNum, tt.filteredNodesNum) 1418 state := framework.NewCycleState() 1419 pl := plugintesting.SetupPlugin(ctx, b, podTopologySpreadFunc, &config.PodTopologySpreadArgs{DefaultingType: config.ListDefaulting}, cache.NewSnapshot(existingPods, allNodes)) 1420 p := pl.(*PodTopologySpread) 1421 1422 status := p.PreScore(ctx, state, tt.pod, tf.BuildNodeInfos(filteredNodes)) 1423 if !status.IsSuccess() { 1424 b.Fatalf("unexpected error: %v", status) 1425 } 1426 b.ResetTimer() 1427 1428 for i := 0; i < b.N; i++ { 1429 var gotList framework.NodeScoreList 1430 for _, n := range filteredNodes { 1431 nodeName := n.Name 1432 score, status := p.Score(context.Background(), state, tt.pod, nodeName) 1433 if !status.IsSuccess() { 1434 b.Fatalf("unexpected error: %v", status) 1435 } 1436 gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score}) 1437 } 1438 1439 status = p.NormalizeScore(context.Background(), state, tt.pod, gotList) 1440 if !status.IsSuccess() { 1441 b.Fatal(status) 1442 } 1443 } 1444 }) 1445 } 1446 }