1
16
17 package interpodaffinity
18
19 import (
20 "context"
21 "reflect"
22 "strings"
23 "testing"
24
25 "github.com/google/go-cmp/cmp"
26 v1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime"
29 "k8s.io/klog/v2/ktesting"
30 "k8s.io/kubernetes/pkg/scheduler/apis/config"
31 "k8s.io/kubernetes/pkg/scheduler/framework"
32 plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing"
33 "k8s.io/kubernetes/pkg/scheduler/internal/cache"
34 tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
35 )
36
37 var nsLabelT1 = map[string]string{"team": "team1"}
38 var nsLabelT2 = map[string]string{"team": "team2"}
39 var namespaces = []runtime.Object{
40 &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam1.team1", Labels: nsLabelT1}},
41 &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam2.team1", Labels: nsLabelT1}},
42 &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam1.team2", Labels: nsLabelT2}},
43 &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam2.team2", Labels: nsLabelT2}},
44 }
45
46 func TestPreferredAffinity(t *testing.T) {
47 labelRgChina := map[string]string{
48 "region": "China",
49 }
50 labelRgIndia := map[string]string{
51 "region": "India",
52 }
53 labelAzAz1 := map[string]string{
54 "az": "az1",
55 }
56 labelAzAz2 := map[string]string{
57 "az": "az2",
58 }
59 labelRgChinaAzAz1 := map[string]string{
60 "region": "China",
61 "az": "az1",
62 }
63 podLabelSecurityS1 := map[string]string{
64 "security": "S1",
65 }
66 podLabelSecurityS2 := map[string]string{
67 "security": "S2",
68 }
69
70 stayWithS1InRegion := &v1.Affinity{
71 PodAffinity: &v1.PodAffinity{
72 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
73 {
74 Weight: 5,
75 PodAffinityTerm: v1.PodAffinityTerm{
76 LabelSelector: &metav1.LabelSelector{
77 MatchExpressions: []metav1.LabelSelectorRequirement{
78 {
79 Key: "security",
80 Operator: metav1.LabelSelectorOpIn,
81 Values: []string{"S1"},
82 },
83 },
84 },
85 TopologyKey: "region",
86 },
87 },
88 },
89 },
90 }
91 stayWithS2InRegion := &v1.Affinity{
92 PodAffinity: &v1.PodAffinity{
93 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
94 {
95 Weight: 6,
96 PodAffinityTerm: v1.PodAffinityTerm{
97 LabelSelector: &metav1.LabelSelector{
98 MatchExpressions: []metav1.LabelSelectorRequirement{
99 {
100 Key: "security",
101 Operator: metav1.LabelSelectorOpIn,
102 Values: []string{"S2"},
103 },
104 },
105 },
106 TopologyKey: "region",
107 },
108 },
109 },
110 },
111 }
112 affinity3 := &v1.Affinity{
113 PodAffinity: &v1.PodAffinity{
114 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
115 {
116 Weight: 8,
117 PodAffinityTerm: v1.PodAffinityTerm{
118 LabelSelector: &metav1.LabelSelector{
119 MatchExpressions: []metav1.LabelSelectorRequirement{
120 {
121 Key: "security",
122 Operator: metav1.LabelSelectorOpNotIn,
123 Values: []string{"S1"},
124 }, {
125 Key: "security",
126 Operator: metav1.LabelSelectorOpIn,
127 Values: []string{"S2"},
128 },
129 },
130 },
131 TopologyKey: "region",
132 },
133 }, {
134 Weight: 2,
135 PodAffinityTerm: v1.PodAffinityTerm{
136 LabelSelector: &metav1.LabelSelector{
137 MatchExpressions: []metav1.LabelSelectorRequirement{
138 {
139 Key: "security",
140 Operator: metav1.LabelSelectorOpExists,
141 }, {
142 Key: "wrongkey",
143 Operator: metav1.LabelSelectorOpDoesNotExist,
144 },
145 },
146 },
147 TopologyKey: "region",
148 },
149 },
150 },
151 },
152 }
153 hardAffinity := &v1.Affinity{
154 PodAffinity: &v1.PodAffinity{
155 RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
156 {
157 LabelSelector: &metav1.LabelSelector{
158 MatchExpressions: []metav1.LabelSelectorRequirement{
159 {
160 Key: "security",
161 Operator: metav1.LabelSelectorOpIn,
162 Values: []string{"S1", "value2"},
163 },
164 },
165 },
166 TopologyKey: "region",
167 }, {
168 LabelSelector: &metav1.LabelSelector{
169 MatchExpressions: []metav1.LabelSelectorRequirement{
170 {
171 Key: "security",
172 Operator: metav1.LabelSelectorOpExists,
173 }, {
174 Key: "wrongkey",
175 Operator: metav1.LabelSelectorOpDoesNotExist,
176 },
177 },
178 },
179 TopologyKey: "region",
180 },
181 },
182 },
183 }
184 awayFromS1InAz := &v1.Affinity{
185 PodAntiAffinity: &v1.PodAntiAffinity{
186 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
187 {
188 Weight: 5,
189 PodAffinityTerm: v1.PodAffinityTerm{
190 LabelSelector: &metav1.LabelSelector{
191 MatchExpressions: []metav1.LabelSelectorRequirement{
192 {
193 Key: "security",
194 Operator: metav1.LabelSelectorOpIn,
195 Values: []string{"S1"},
196 },
197 },
198 },
199 TopologyKey: "az",
200 },
201 },
202 },
203 },
204 }
205
206 awayFromS2InAz := &v1.Affinity{
207 PodAntiAffinity: &v1.PodAntiAffinity{
208 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
209 {
210 Weight: 5,
211 PodAffinityTerm: v1.PodAffinityTerm{
212 LabelSelector: &metav1.LabelSelector{
213 MatchExpressions: []metav1.LabelSelectorRequirement{
214 {
215 Key: "security",
216 Operator: metav1.LabelSelectorOpIn,
217 Values: []string{"S2"},
218 },
219 },
220 },
221 TopologyKey: "az",
222 },
223 },
224 },
225 },
226 }
227
228 stayWithS1InRegionAwayFromS2InAz := &v1.Affinity{
229 PodAffinity: &v1.PodAffinity{
230 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
231 {
232 Weight: 8,
233 PodAffinityTerm: v1.PodAffinityTerm{
234 LabelSelector: &metav1.LabelSelector{
235 MatchExpressions: []metav1.LabelSelectorRequirement{
236 {
237 Key: "security",
238 Operator: metav1.LabelSelectorOpIn,
239 Values: []string{"S1"},
240 },
241 },
242 },
243 TopologyKey: "region",
244 },
245 },
246 },
247 },
248 PodAntiAffinity: &v1.PodAntiAffinity{
249 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
250 {
251 Weight: 5,
252 PodAffinityTerm: v1.PodAffinityTerm{
253 LabelSelector: &metav1.LabelSelector{
254 MatchExpressions: []metav1.LabelSelectorRequirement{
255 {
256 Key: "security",
257 Operator: metav1.LabelSelectorOpIn,
258 Values: []string{"S2"},
259 },
260 },
261 },
262 TopologyKey: "az",
263 },
264 },
265 },
266 },
267 }
268
269 affinityNamespaceSelector := &v1.Affinity{
270 PodAffinity: &v1.PodAffinity{
271 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
272 {
273 Weight: 5,
274 PodAffinityTerm: v1.PodAffinityTerm{
275 LabelSelector: &metav1.LabelSelector{
276 MatchExpressions: []metav1.LabelSelectorRequirement{
277 {
278 Key: "security",
279 Operator: metav1.LabelSelectorOpIn,
280 Values: []string{"S1"},
281 },
282 },
283 },
284 TopologyKey: "region",
285 Namespaces: []string{"subteam2.team2"},
286 NamespaceSelector: &metav1.LabelSelector{
287 MatchExpressions: []metav1.LabelSelectorRequirement{
288 {
289 Key: "team",
290 Operator: metav1.LabelSelectorOpIn,
291 Values: []string{"team1"},
292 },
293 },
294 },
295 },
296 },
297 },
298 },
299 }
300 antiAffinityNamespaceSelector := &v1.Affinity{
301 PodAntiAffinity: &v1.PodAntiAffinity{
302 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
303 {
304 Weight: 5,
305 PodAffinityTerm: v1.PodAffinityTerm{
306 LabelSelector: &metav1.LabelSelector{
307 MatchExpressions: []metav1.LabelSelectorRequirement{
308 {
309 Key: "security",
310 Operator: metav1.LabelSelectorOpIn,
311 Values: []string{"S1"},
312 },
313 },
314 },
315 TopologyKey: "region",
316 Namespaces: []string{"subteam2.team2"},
317 NamespaceSelector: &metav1.LabelSelector{
318 MatchExpressions: []metav1.LabelSelectorRequirement{
319 {
320 Key: "team",
321 Operator: metav1.LabelSelectorOpIn,
322 Values: []string{"team1"},
323 },
324 },
325 },
326 },
327 },
328 },
329 },
330 }
331 invalidAffinityLabels := &v1.Affinity{
332 PodAffinity: &v1.PodAffinity{
333 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
334 {
335 Weight: 8,
336 PodAffinityTerm: v1.PodAffinityTerm{
337 LabelSelector: &metav1.LabelSelector{
338 MatchExpressions: []metav1.LabelSelectorRequirement{
339 {
340 Key: "security",
341 Operator: metav1.LabelSelectorOpIn,
342 Values: []string{"{{.bad-value.}}"},
343 },
344 },
345 },
346 TopologyKey: "region",
347 },
348 },
349 },
350 },
351 }
352 invalidAntiAffinityLabels := &v1.Affinity{
353 PodAntiAffinity: &v1.PodAntiAffinity{
354 PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
355 {
356 Weight: 5,
357 PodAffinityTerm: v1.PodAffinityTerm{
358 LabelSelector: &metav1.LabelSelector{
359 MatchExpressions: []metav1.LabelSelectorRequirement{
360 {
361 Key: "security",
362 Operator: metav1.LabelSelectorOpIn,
363 Values: []string{"{{.bad-value.}}"},
364 },
365 },
366 },
367 TopologyKey: "az",
368 },
369 },
370 },
371 },
372 }
373
374 tests := []struct {
375 pod *v1.Pod
376 pods []*v1.Pod
377 nodes []*v1.Node
378 expectedList framework.NodeScoreList
379 name string
380 ignorePreferredTermsOfExistingPods bool
381 wantStatus *framework.Status
382 }{
383 {
384 name: "all nodes are same priority as Affinity is nil",
385 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
386 nodes: []*v1.Node{
387 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
388 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
389 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
390 },
391 wantStatus: framework.NewStatus(framework.Skip),
392 },
393
394
395
396 {
397 name: "Affinity: pod that matches topology key & pods in nodes will get high score comparing to others" +
398 "which doesn't match either pods in nodes or in topology key",
399 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
400 pods: []*v1.Pod{
401 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
402 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
403 {Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
404 },
405 nodes: []*v1.Node{
406 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
407 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
408 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
409 },
410 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: 0}},
411 },
412
413
414
415
416 {
417 name: "All the nodes that have the same topology key & label value with one of them has an existing pod that match the affinity rules, have the same score",
418 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}},
419 pods: []*v1.Pod{
420 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
421 },
422 nodes: []*v1.Node{
423 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
424 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChinaAzAz1}},
425 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}},
426 },
427 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
428 },
429
430
431
432
433 {
434 name: "Affinity: nodes in one region has more matching pods comparing to other region, so the region which has more matches will get high score",
435 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
436 pods: []*v1.Pod{
437 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
438 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
439 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
440 {Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
441 {Spec: v1.PodSpec{NodeName: "node4"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
442 {Spec: v1.PodSpec{NodeName: "node5"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
443 },
444 nodes: []*v1.Node{
445 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
446 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
447 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgChina}},
448 {ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelRgChina}},
449 {ObjectMeta: metav1.ObjectMeta{Name: "node5", Labels: labelRgIndia}},
450 },
451 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: framework.MaxNodeScore}, {Name: "node5", Score: 0}},
452 },
453
454 {
455 name: "Affinity: different Label operators and values for pod affinity scheduling preference, including some match failures ",
456 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: affinity3}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
457 pods: []*v1.Pod{
458 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
459 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
460 {Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
461 },
462 nodes: []*v1.Node{
463 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
464 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
465 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
466 },
467 expectedList: []framework.NodeScore{{Name: "node1", Score: 20}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
468 },
469
470
471 {
472 name: "Affinity symmetry: considered only the preferredDuringSchedulingIgnoredDuringExecution in pod affinity symmetry",
473 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
474 pods: []*v1.Pod{
475 {Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
476 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
477 },
478 nodes: []*v1.Node{
479 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
480 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
481 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
482 },
483 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
484 },
485 {
486 name: "Affinity symmetry with namespace selector",
487 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
488 pods: []*v1.Pod{
489 {Spec: v1.PodSpec{NodeName: "node1", Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
490 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
491 },
492 nodes: []*v1.Node{
493 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
494 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
495 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
496 },
497 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: 0}},
498 },
499 {
500 name: "AntiAffinity symmetry with namespace selector",
501 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
502 pods: []*v1.Pod{
503 {Spec: v1.PodSpec{NodeName: "node1", Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
504 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
505 },
506 nodes: []*v1.Node{
507 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
508 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
509 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
510 },
511 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: framework.MaxNodeScore}},
512 },
513 {
514 name: "Affinity symmetry: considered RequiredDuringSchedulingIgnoredDuringExecution in pod affinity symmetry",
515 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
516 pods: []*v1.Pod{
517 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardAffinity}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
518 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardAffinity}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
519 },
520 nodes: []*v1.Node{
521 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
522 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
523 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
524 },
525 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
526 },
527
528
529
530
531
532
533
534 {
535 name: "Anti Affinity: pod that does not match existing pods in node will get high score ",
536 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
537 pods: []*v1.Pod{
538 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
539 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
540 },
541 nodes: []*v1.Node{
542 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
543 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
544 },
545 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
546 },
547 {
548 name: "Anti Affinity: pod that does not match topology key & match the pods in nodes will get higher score comparing to others ",
549 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
550 pods: []*v1.Pod{
551 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
552 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
553 },
554 nodes: []*v1.Node{
555 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
556 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
557 },
558 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
559 },
560 {
561 name: "Anti Affinity: one node has more matching pods comparing to other node, so the node which has more unmatches will get high score",
562 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
563 pods: []*v1.Pod{
564 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
565 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
566 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
567 },
568 nodes: []*v1.Node{
569 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
570 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
571 },
572 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
573 },
574
575 {
576 name: "Anti Affinity symmetry: the existing pods in node which has anti affinity match will get high score",
577 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
578 pods: []*v1.Pod{
579 {Spec: v1.PodSpec{NodeName: "node1", Affinity: awayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
580 {Spec: v1.PodSpec{NodeName: "node2", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
581 },
582 nodes: []*v1.Node{
583 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
584 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz2}},
585 },
586 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
587 },
588
589 {
590 name: "Affinity and Anti Affinity: considered only preferredDuringSchedulingIgnoredDuringExecution in both pod affinity & anti affinity",
591 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
592 pods: []*v1.Pod{
593 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
594 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
595 },
596 nodes: []*v1.Node{
597 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
598 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz1}},
599 },
600 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
601 },
602
603
604
605
606 {
607 name: "Affinity and Anti Affinity: considering both affinity and anti-affinity, the pod to schedule and existing pods have the same labels",
608 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
609 pods: []*v1.Pod{
610 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
611 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
612 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
613 {Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
614 {Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
615 {Spec: v1.PodSpec{NodeName: "node4"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
616 {Spec: v1.PodSpec{NodeName: "node5"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
617 },
618 nodes: []*v1.Node{
619 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChinaAzAz1}},
620 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
621 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgChina}},
622 {ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelRgChina}},
623 {ObjectMeta: metav1.ObjectMeta{Name: "node5", Labels: labelRgIndia}},
624 },
625 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: framework.MaxNodeScore}, {Name: "node5", Score: 0}},
626 },
627
628
629
630
631
632 {
633 name: "Affinity and Anti Affinity and symmetry: considered only preferredDuringSchedulingIgnoredDuringExecution in both pod affinity & anti affinity & symmetry",
634 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
635 pods: []*v1.Pod{
636 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
637 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
638 {Spec: v1.PodSpec{NodeName: "node3", Affinity: stayWithS1InRegionAwayFromS2InAz}},
639 {Spec: v1.PodSpec{NodeName: "node4", Affinity: awayFromS1InAz}},
640 },
641 nodes: []*v1.Node{
642 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
643 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz1}},
644 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}},
645 {ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelAzAz2}},
646 },
647 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: 0}},
648 },
649
650
651
652 {
653 name: "Avoid panic when partial nodes in a topology don't have pods with affinity",
654 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
655 pods: []*v1.Pod{
656 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
657 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS1InRegionAwayFromS2InAz}},
658 },
659 nodes: []*v1.Node{
660 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
661 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
662 },
663 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
664 },
665 {
666 name: "invalid Affinity fails PreScore",
667 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: invalidAffinityLabels}},
668 wantStatus: framework.NewStatus(framework.Error, `Invalid value: "{{.bad-value.}}"`),
669 nodes: []*v1.Node{
670 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
671 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
672 },
673 },
674 {
675 name: "invalid AntiAffinity fails PreScore",
676 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: invalidAntiAffinityLabels}},
677 wantStatus: framework.NewStatus(framework.Error, `Invalid value: "{{.bad-value.}}"`),
678 nodes: []*v1.Node{
679 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
680 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
681 },
682 },
683 {
684 name: "Affinity with pods matching NamespaceSelector",
685 pod: &v1.Pod{Spec: v1.PodSpec{Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
686 pods: []*v1.Pod{
687 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
688 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
689 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelSecurityS1}},
690 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
691 },
692 nodes: []*v1.Node{
693 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
694 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
695 },
696 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
697 },
698 {
699 name: "Affinity with pods matching both NamespaceSelector and Namespaces fields",
700 pod: &v1.Pod{Spec: v1.PodSpec{Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
701 pods: []*v1.Pod{
702 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
703 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
704 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelSecurityS1}},
705 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
706 },
707 nodes: []*v1.Node{
708 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
709 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
710 },
711 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
712 },
713 {
714 name: "Affinity with pods matching NamespaceSelector",
715 pod: &v1.Pod{Spec: v1.PodSpec{Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
716 pods: []*v1.Pod{
717 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
718 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
719 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelSecurityS1}},
720 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
721 },
722 nodes: []*v1.Node{
723 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
724 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
725 },
726 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
727 },
728 {
729 name: "Affinity with pods matching both NamespaceSelector and Namespaces fields",
730 pod: &v1.Pod{Spec: v1.PodSpec{Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
731 pods: []*v1.Pod{
732 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
733 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
734 {Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelSecurityS1}},
735 {Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
736 },
737 nodes: []*v1.Node{
738 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
739 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
740 },
741 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
742 },
743 {
744 name: "Ignore preferred terms of existing pods",
745 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
746 pods: []*v1.Pod{
747 {Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
748 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
749 },
750 nodes: []*v1.Node{
751 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
752 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
753 },
754 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
755 wantStatus: framework.NewStatus(framework.Skip),
756 ignorePreferredTermsOfExistingPods: true,
757 },
758 {
759 name: "Do not ignore preferred terms of existing pods",
760 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
761 pods: []*v1.Pod{
762 {Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
763 {Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
764 },
765 nodes: []*v1.Node{
766 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
767 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
768 },
769 expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
770 ignorePreferredTermsOfExistingPods: false,
771 },
772 {
773 name: "No nodes to score",
774 pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
775 pods: []*v1.Pod{},
776 nodes: []*v1.Node{},
777 wantStatus: framework.NewStatus(framework.Skip),
778 },
779 }
780 for _, test := range tests {
781 t.Run(test.name, func(t *testing.T) {
782 _, ctx := ktesting.NewTestContext(t)
783 ctx, cancel := context.WithCancel(ctx)
784 defer cancel()
785 state := framework.NewCycleState()
786 p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{HardPodAffinityWeight: 1, IgnorePreferredTermsOfExistingPods: test.ignorePreferredTermsOfExistingPods}, cache.NewSnapshot(test.pods, test.nodes), namespaces)
787 status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.pod, tf.BuildNodeInfos(test.nodes))
788
789 if !status.IsSuccess() {
790 if status.Code() != test.wantStatus.Code() {
791 t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Code got: %v, want: %v", status.Code(), test.wantStatus.Code())
792 }
793
794 if !strings.Contains(status.Message(), test.wantStatus.Message()) {
795 t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Message got: %v, want: %v", status.Message(), test.wantStatus.Message())
796 }
797 return
798 }
799
800 var gotList framework.NodeScoreList
801 for _, n := range test.nodes {
802 nodeName := n.ObjectMeta.Name
803 score, status := p.(framework.ScorePlugin).Score(ctx, state, test.pod, nodeName)
804 if !status.IsSuccess() {
805 t.Errorf("unexpected error from Score: %v", status)
806 }
807 gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score})
808 }
809
810 status = p.(framework.ScorePlugin).ScoreExtensions().NormalizeScore(ctx, state, test.pod, gotList)
811 if !status.IsSuccess() {
812 t.Errorf("unexpected error from NormalizeScore: %v", status)
813 }
814
815 if diff := cmp.Diff(test.expectedList, gotList); diff != "" {
816 t.Errorf("node score list doesn't match (-want,+got): \n %s", diff)
817 }
818 })
819 }
820 }
821
822 func TestPreferredAffinityWithHardPodAffinitySymmetricWeight(t *testing.T) {
823 podLabelServiceS1 := map[string]string{
824 "service": "S1",
825 }
826 labelRgChina := map[string]string{
827 "region": "China",
828 }
829 labelRgIndia := map[string]string{
830 "region": "India",
831 }
832 labelAzAz1 := map[string]string{
833 "az": "az1",
834 }
835 hardPodAffinity := &v1.Affinity{
836 PodAffinity: &v1.PodAffinity{
837 RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
838 {
839 LabelSelector: &metav1.LabelSelector{
840 MatchExpressions: []metav1.LabelSelectorRequirement{
841 {
842 Key: "service",
843 Operator: metav1.LabelSelectorOpIn,
844 Values: []string{"S1"},
845 },
846 },
847 },
848 Namespaces: []string{"", "subteam2.team2"},
849 NamespaceSelector: &metav1.LabelSelector{
850 MatchExpressions: []metav1.LabelSelectorRequirement{
851 {
852 Key: "team",
853 Operator: metav1.LabelSelectorOpIn,
854 Values: []string{"team1"},
855 },
856 },
857 },
858 TopologyKey: "region",
859 },
860 },
861 },
862 }
863 tests := []struct {
864 pod *v1.Pod
865 pods []*v1.Pod
866 nodes []*v1.Node
867 hardPodAffinityWeight int32
868 expectedList framework.NodeScoreList
869 name string
870 wantStatus *framework.Status
871 }{
872 {
873 name: "with default weight",
874 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: podLabelServiceS1}},
875 pods: []*v1.Pod{
876 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
877 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
878 },
879 nodes: []*v1.Node{
880 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
881 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
882 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
883 },
884 hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
885 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
886 },
887 {
888 name: "with zero weight",
889 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: podLabelServiceS1}},
890 pods: []*v1.Pod{
891 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
892 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
893 },
894 nodes: []*v1.Node{
895 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
896 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
897 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
898 },
899 hardPodAffinityWeight: 0,
900 wantStatus: framework.NewStatus(framework.Skip),
901 },
902 {
903 name: "with no matching namespace",
904 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelServiceS1}},
905 pods: []*v1.Pod{
906 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
907 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
908 },
909 nodes: []*v1.Node{
910 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
911 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
912 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
913 },
914 hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
915 wantStatus: framework.NewStatus(framework.Skip),
916 },
917 {
918 name: "with matching NamespaceSelector",
919 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelServiceS1}},
920 pods: []*v1.Pod{
921 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
922 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
923 },
924 nodes: []*v1.Node{
925 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
926 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
927 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
928 },
929 hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
930 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
931 },
932 {
933 name: "with matching Namespaces",
934 pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelServiceS1}},
935 pods: []*v1.Pod{
936 {Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
937 {Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
938 },
939 nodes: []*v1.Node{
940 {ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
941 {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
942 {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
943 },
944 hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
945 expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
946 },
947 }
948 for _, test := range tests {
949 t.Run(test.name, func(t *testing.T) {
950 _, ctx := ktesting.NewTestContext(t)
951 ctx, cancel := context.WithCancel(ctx)
952 defer cancel()
953 state := framework.NewCycleState()
954 p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{HardPodAffinityWeight: test.hardPodAffinityWeight}, cache.NewSnapshot(test.pods, test.nodes), namespaces)
955 status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.pod, tf.BuildNodeInfos(test.nodes))
956 if !test.wantStatus.Equal(status) {
957 t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Code got: %v, want: %v", status.Code(), test.wantStatus.Code())
958 }
959 if !status.IsSuccess() {
960 return
961 }
962
963 var gotList framework.NodeScoreList
964 for _, n := range test.nodes {
965 nodeName := n.ObjectMeta.Name
966 score, status := p.(framework.ScorePlugin).Score(ctx, state, test.pod, nodeName)
967 if !status.IsSuccess() {
968 t.Errorf("unexpected error: %v", status)
969 }
970 gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score})
971 }
972
973 status = p.(framework.ScorePlugin).ScoreExtensions().NormalizeScore(ctx, state, test.pod, gotList)
974 if !status.IsSuccess() {
975 t.Errorf("unexpected error: %v", status)
976 }
977
978 if !reflect.DeepEqual(test.expectedList, gotList) {
979 t.Errorf("expected:\n\t%+v,\ngot:\n\t%+v", test.expectedList, gotList)
980 }
981 })
982 }
983 }
984
View as plain text