1
2
3
4
5
6
7
8
9
10
11
12
13
14 package pod
15
16 import (
17 "context"
18 "testing"
19
20 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
21
22 . "github.com/onsi/gomega"
23
24 "github.com/chaos-mesh/chaos-mesh/pkg/label"
25 . "github.com/chaos-mesh/chaos-mesh/pkg/testutils"
26
27 v1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/labels"
30
31 "sigs.k8s.io/controller-runtime/pkg/client"
32 "sigs.k8s.io/controller-runtime/pkg/client/fake"
33 )
34
35 func TestSelectPods(t *testing.T) {
36 g := NewGomegaWithT(t)
37
38 objects, pods := GenerateNPods("p", 5, PodArg{Labels: map[string]string{"l1": "l1"}, Nodename: "az1-node1"})
39 objects2, pods2 := GenerateNPods("s", 2, PodArg{Namespace: "test-s", Labels: map[string]string{"l2": "l2"}, Nodename: "az2-node1"})
40
41 objects3, _ := GenerateNNodes("az1-node", 3, map[string]string{"disktype": "ssd", "zone": "az1"})
42 objects4, _ := GenerateNNodes("az2-node", 2, map[string]string{"disktype": "hdd", "zone": "az2"})
43
44 objects = append(objects, objects2...)
45 objects = append(objects, objects3...)
46 objects = append(objects, objects4...)
47
48 pods = append(pods, pods2...)
49
50 c := fake.NewFakeClient(objects...)
51 var r client.Reader
52
53 type TestCase struct {
54 name string
55 selector v1alpha1.PodSelectorSpec
56 expectedPods []v1.Pod
57 }
58
59 tcs := []TestCase{
60 {
61 name: "filter specified pods",
62 selector: v1alpha1.PodSelectorSpec{
63 Pods: map[string][]string{
64 metav1.NamespaceDefault: {"p3", "p4"},
65 "test-s": {"s1"},
66 },
67 },
68 expectedPods: []v1.Pod{pods[3], pods[4], pods[6]},
69 },
70 {
71 name: "filter labels pods",
72 selector: v1alpha1.PodSelectorSpec{
73 LabelSelectors: map[string]string{"l2": "l2"},
74 },
75 expectedPods: []v1.Pod{pods[5], pods[6]},
76 },
77 {
78 name: "filter pods by label expressions",
79 selector: v1alpha1.PodSelectorSpec{
80 ExpressionSelectors: []metav1.LabelSelectorRequirement{
81 {
82 Key: "l2",
83 Operator: metav1.LabelSelectorOpIn,
84 Values: []string{"l2"},
85 },
86 },
87 },
88 expectedPods: []v1.Pod{pods[5], pods[6]},
89 },
90 {
91 name: "filter pods by label selectors and expression selectors",
92 selector: v1alpha1.PodSelectorSpec{
93 LabelSelectors: map[string]string{"l1": "l1"},
94 ExpressionSelectors: []metav1.LabelSelectorRequirement{
95 {
96 Key: "l2",
97 Operator: metav1.LabelSelectorOpIn,
98 Values: []string{"l2"},
99 },
100 },
101 },
102 expectedPods: nil,
103 },
104 {
105 name: "filter namespace and labels",
106 selector: v1alpha1.PodSelectorSpec{
107 Namespaces: []string{"test-s"},
108 LabelSelectors: map[string]string{"l2": "l2"},
109 },
110 expectedPods: []v1.Pod{pods[5], pods[6]},
111 },
112 {
113 name: "filter namespace and labels",
114 selector: v1alpha1.PodSelectorSpec{
115 Namespaces: []string{metav1.NamespaceDefault},
116 LabelSelectors: map[string]string{"l2": "l2"},
117 },
118 expectedPods: nil,
119 },
120 {
121 name: "filter by specified node",
122 selector: v1alpha1.PodSelectorSpec{
123 Nodes: []string{"az1-node1"},
124 },
125 expectedPods: []v1.Pod{pods[0], pods[1], pods[2], pods[3], pods[4]},
126 },
127 {
128 name: "filter node and labels",
129 selector: v1alpha1.PodSelectorSpec{
130 LabelSelectors: map[string]string{"l1": "l1"},
131 Nodes: []string{"az2-node1"},
132 },
133 expectedPods: nil,
134 },
135 {
136 name: "filter pods by nodeSelector",
137 selector: v1alpha1.PodSelectorSpec{
138 NodeSelectors: map[string]string{"disktype": "hdd"},
139 },
140 expectedPods: []v1.Pod{pods[5], pods[6]},
141 },
142 {
143 name: "filter pods by node and nodeSelector",
144 selector: v1alpha1.PodSelectorSpec{
145 NodeSelectors: map[string]string{"zone": "az1"},
146 Nodes: []string{"az2-node1"},
147 },
148 expectedPods: []v1.Pod{pods[0], pods[1], pods[2], pods[3], pods[4], pods[5], pods[6]},
149 },
150 }
151
152 var (
153 testCfgClusterScoped = true
154 testCfgTargetNamespace = ""
155 )
156
157 for _, tc := range tcs {
158 filteredPods, err := SelectPods(context.Background(), c, r, tc.selector, testCfgClusterScoped, testCfgTargetNamespace, false)
159 g.Expect(err).ShouldNot(HaveOccurred(), tc.name)
160 g.Expect(len(filteredPods)).To(Equal(len(tc.expectedPods)), tc.name)
161 }
162 }
163
164 func TestCheckPodMeetSelector(t *testing.T) {
165 g := NewGomegaWithT(t)
166
167 type TestCase struct {
168 name string
169 selector v1alpha1.PodSelectorSpec
170 pod v1.Pod
171 expectedValue bool
172 }
173
174 tcs := []TestCase{
175 {
176 name: "meet label",
177 pod: NewPod(PodArg{Name: "t1", Status: v1.PodPending, Labels: map[string]string{"app": "tikv", "ss": "t1"}}),
178 selector: v1alpha1.PodSelectorSpec{
179 LabelSelectors: map[string]string{"app": "tikv"},
180 },
181 expectedValue: true,
182 },
183 {
184 name: "not meet label",
185 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb", "ss": "t1"}}),
186 selector: v1alpha1.PodSelectorSpec{
187 LabelSelectors: map[string]string{"app": "tikv"},
188 },
189 expectedValue: false,
190 },
191 {
192 name: "pod labels is empty",
193 pod: NewPod(PodArg{Name: "t1"}),
194 selector: v1alpha1.PodSelectorSpec{
195 LabelSelectors: map[string]string{"app": "tikv"},
196 },
197 expectedValue: false,
198 },
199 {
200 name: "selector is empty",
201 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
202 selector: v1alpha1.PodSelectorSpec{},
203 expectedValue: true,
204 },
205 {
206 name: "meet labels and meet expressions",
207 pod: NewPod(PodArg{Name: "t1", Status: v1.PodPending, Labels: map[string]string{"app": "tikv", "ss": "t1"}}),
208 selector: v1alpha1.PodSelectorSpec{
209 LabelSelectors: map[string]string{"app": "tikv"},
210 ExpressionSelectors: []metav1.LabelSelectorRequirement{
211 {
212 Key: "ss",
213 Operator: metav1.LabelSelectorOpExists,
214 },
215 },
216 },
217 expectedValue: true,
218 },
219 {
220 name: "meet labels and not meet expressions",
221 pod: NewPod(PodArg{Name: "t1", Status: v1.PodPending, Labels: map[string]string{"app": "tikv", "ss": "t1"}}),
222 selector: v1alpha1.PodSelectorSpec{
223 LabelSelectors: map[string]string{"app": "tikv"},
224 ExpressionSelectors: []metav1.LabelSelectorRequirement{
225 {
226 Key: "ss",
227 Operator: metav1.LabelSelectorOpNotIn,
228 Values: []string{"t1"},
229 },
230 },
231 },
232 expectedValue: false,
233 },
234 {
235 name: "meet namespace",
236 pod: NewPod(PodArg{Name: "t1"}),
237 selector: v1alpha1.PodSelectorSpec{
238 Namespaces: []string{metav1.NamespaceDefault},
239 },
240 expectedValue: true,
241 },
242 {
243 name: "meet namespace and meet labels",
244 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tikv"}}),
245 selector: v1alpha1.PodSelectorSpec{
246 Namespaces: []string{metav1.NamespaceDefault},
247 LabelSelectors: map[string]string{"app": "tikv"},
248 },
249 expectedValue: true,
250 },
251 {
252 name: "meet namespace and not meet labels",
253 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
254 selector: v1alpha1.PodSelectorSpec{
255 Namespaces: []string{metav1.NamespaceDefault},
256 LabelSelectors: map[string]string{"app": "tikv"},
257 },
258 expectedValue: false,
259 },
260 {
261 name: "meet pods",
262 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
263 selector: v1alpha1.PodSelectorSpec{
264 Pods: map[string][]string{
265 metav1.NamespaceDefault: {"t1"},
266 },
267 },
268 expectedValue: true,
269 },
270 {
271 name: "meet annotation",
272 pod: NewPod(PodArg{Name: "t1", Ans: map[string]string{"an": "n1", "an2": "n2"}, Labels: map[string]string{"app": "tidb"}}),
273 selector: v1alpha1.PodSelectorSpec{
274 Namespaces: []string{metav1.NamespaceDefault},
275 AnnotationSelectors: map[string]string{
276 "an": "n1",
277 },
278 },
279 expectedValue: true,
280 },
281 {
282 name: "not meet annotation",
283 pod: NewPod(PodArg{Name: "t1", Ans: map[string]string{"an": "n1"}, Labels: map[string]string{"app": "tidb"}}),
284 selector: v1alpha1.PodSelectorSpec{
285 Namespaces: []string{metav1.NamespaceDefault},
286 AnnotationSelectors: map[string]string{
287 "an": "n2",
288 },
289 },
290 expectedValue: false,
291 },
292 {
293 name: "meet pod selector",
294 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
295 selector: v1alpha1.PodSelectorSpec{
296 Pods: map[string][]string{
297 metav1.NamespaceDefault: {"t1", "t2"},
298 },
299 },
300 expectedValue: true,
301 },
302 {
303 name: "not meet pod selector",
304 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
305 selector: v1alpha1.PodSelectorSpec{
306 Pods: map[string][]string{
307 metav1.NamespaceDefault: {"t2"},
308 },
309 },
310 expectedValue: false,
311 },
312 {
313 name: "meet pod selector and not meet labels",
314 pod: NewPod(PodArg{Name: "t1", Labels: map[string]string{"app": "tidb"}}),
315 selector: v1alpha1.PodSelectorSpec{
316 Pods: map[string][]string{
317 metav1.NamespaceDefault: {"t1", "t2"},
318 },
319 LabelSelectors: map[string]string{"app": "tikv"},
320 },
321 expectedValue: false,
322 },
323 }
324
325 for _, tc := range tcs {
326 meet, err := CheckPodMeetSelector(tc.pod, tc.selector)
327 g.Expect(err).ShouldNot(HaveOccurred(), tc.name)
328 g.Expect(meet).To(Equal(tc.expectedValue), tc.name)
329 }
330 }
331
332 func TestRandomFixedIndexes(t *testing.T) {
333 g := NewGomegaWithT(t)
334
335 type TestCase struct {
336 name string
337 start uint
338 end uint
339 count uint
340 expectedOutputLen int
341 }
342
343 tcs := []TestCase{
344 {
345 name: "start 0, end 10, count 3",
346 start: 0,
347 end: 10,
348 count: 3,
349 expectedOutputLen: 3,
350 },
351 {
352 name: "start 0, end 10, count 12",
353 start: 0,
354 end: 10,
355 count: 12,
356 expectedOutputLen: 10,
357 },
358 {
359 name: "start 5, end 10, count 3",
360 start: 5,
361 end: 10,
362 count: 3,
363 expectedOutputLen: 3,
364 },
365 }
366
367 for _, tc := range tcs {
368 values := RandomFixedIndexes(tc.start, tc.end, tc.count)
369 g.Expect(len(values)).To(Equal(tc.expectedOutputLen), tc.name)
370
371 for _, v := range values {
372 g.Expect(v).Should(BeNumerically(">=", tc.start), tc.name)
373 g.Expect(v).Should(BeNumerically("<", tc.end), tc.name)
374 }
375 }
376 }
377
378 func TestFilterByPhaseSelector(t *testing.T) {
379 g := NewGomegaWithT(t)
380
381 type TestCase struct {
382 name string
383 pods []v1.Pod
384 filterSelector labels.Selector
385 filteredPods []v1.Pod
386 }
387
388 pods := []v1.Pod{
389 NewPod(PodArg{Name: "p1", Status: v1.PodRunning}),
390 NewPod(PodArg{Name: "p2", Status: v1.PodRunning}),
391 NewPod(PodArg{Name: "p3", Status: v1.PodPending}),
392 NewPod(PodArg{Name: "p4", Status: v1.PodFailed}),
393 }
394
395 var tcs []TestCase
396
397 runningSelector, err := parseSelector(string(pods[1].Status.Phase))
398 g.Expect(err).ShouldNot(HaveOccurred())
399
400 tcs = append(tcs, TestCase{
401 name: "filter n2",
402 pods: pods,
403 filterSelector: runningSelector,
404 filteredPods: []v1.Pod{pods[0], pods[1]},
405 })
406
407 emptySelector, err := parseSelector("")
408 g.Expect(err).ShouldNot(HaveOccurred())
409 tcs = append(tcs, TestCase{
410 name: "filter empty selector",
411 pods: pods,
412 filterSelector: emptySelector,
413 filteredPods: pods,
414 })
415
416 tcs = append(tcs, TestCase{
417 name: "filter no pods",
418 pods: []v1.Pod{},
419 filterSelector: runningSelector,
420 filteredPods: nil,
421 })
422
423 runningAndPendingSelector, err := parseSelector("Running,Pending")
424 g.Expect(err).ShouldNot(HaveOccurred())
425
426 tcs = append(tcs, TestCase{
427 name: "filter running and pending",
428 pods: pods,
429 filterSelector: runningAndPendingSelector,
430 filteredPods: []v1.Pod{pods[0], pods[1], pods[2]},
431 })
432
433 failedSelector, err := parseSelector("Failed")
434 g.Expect(err).ShouldNot(HaveOccurred())
435
436 tcs = append(tcs, TestCase{
437 name: "filter failed",
438 pods: pods,
439 filterSelector: failedSelector,
440 filteredPods: []v1.Pod{pods[3]},
441 })
442
443 unknownSelector, err := parseSelector("Unknown")
444 g.Expect(err).ShouldNot(HaveOccurred())
445 tcs = append(tcs, TestCase{
446 name: "filter Unknown",
447 pods: pods,
448 filterSelector: unknownSelector,
449 filteredPods: nil,
450 })
451
452 for _, tc := range tcs {
453 g.Expect(filterByPhaseSelector(tc.pods, tc.filterSelector)).To(Equal(tc.filteredPods), tc.name)
454 }
455 }
456
457 func TestFilterByAnnotations(t *testing.T) {
458 g := NewGomegaWithT(t)
459
460 type TestCase struct {
461 name string
462 pods []v1.Pod
463 filterSelector labels.Selector
464 filteredPods []v1.Pod
465 }
466
467 pods := []v1.Pod{
468 NewPod(PodArg{Name: "p1", Ans: map[string]string{"p1": "p1"}}),
469 NewPod(PodArg{Name: "p2", Ans: map[string]string{"p2": "p2"}}),
470 NewPod(PodArg{Name: "p3", Ans: map[string]string{"t": "t"}}),
471 NewPod(PodArg{Name: "p4", Ans: map[string]string{"t": "t"}}),
472 }
473
474 var tcs []TestCase
475 p2Selector, err := parseSelector(label.Label(pods[1].Annotations).String())
476 g.Expect(err).ShouldNot(HaveOccurred())
477
478 tcs = append(tcs, TestCase{
479 name: "filter p2",
480 pods: pods,
481 filterSelector: p2Selector,
482 filteredPods: []v1.Pod{pods[1]},
483 })
484
485 emptySelector, err := parseSelector(label.Label(map[string]string{}).String())
486 g.Expect(err).ShouldNot(HaveOccurred())
487 tcs = append(tcs, TestCase{
488 name: "filter empty selector",
489 pods: pods,
490 filterSelector: emptySelector,
491 filteredPods: pods,
492 })
493
494 tcs = append(tcs, TestCase{
495 name: "filter no pods",
496 pods: []v1.Pod{},
497 filterSelector: p2Selector,
498 filteredPods: nil,
499 })
500
501 for _, tc := range tcs {
502 g.Expect(filterByAnnotations(tc.pods, tc.filterSelector)).To(Equal(tc.filteredPods), tc.name)
503 }
504 }
505
506 func TestFilterNamespaceSelector(t *testing.T) {
507 g := NewGomegaWithT(t)
508
509 type TestCase struct {
510 name string
511 pods []v1.Pod
512 filterSelector labels.Selector
513 filteredPods []v1.Pod
514 }
515
516 pods := []v1.Pod{
517 NewPod(PodArg{Name: "p1", Namespace: "n1"}),
518 NewPod(PodArg{Name: "p2", Namespace: "n2"}),
519 NewPod(PodArg{Name: "p3", Namespace: "n2"}),
520 NewPod(PodArg{Name: "p4", Namespace: "n4"}),
521 }
522
523 var tcs []TestCase
524 n2Selector, err := parseSelector(pods[1].Namespace)
525 g.Expect(err).ShouldNot(HaveOccurred())
526
527 tcs = append(tcs, TestCase{
528 name: "filter n2",
529 pods: pods,
530 filterSelector: n2Selector,
531 filteredPods: []v1.Pod{pods[1], pods[2]},
532 })
533
534 emptySelector, err := parseSelector("")
535 g.Expect(err).ShouldNot(HaveOccurred())
536 tcs = append(tcs, TestCase{
537 name: "filter empty selector",
538 pods: pods,
539 filterSelector: emptySelector,
540 filteredPods: pods,
541 })
542
543 tcs = append(tcs, TestCase{
544 name: "filter no pods",
545 pods: []v1.Pod{},
546 filterSelector: n2Selector,
547 filteredPods: nil,
548 })
549
550 n2AndN3Selector, err := parseSelector("n2,n3")
551 g.Expect(err).ShouldNot(HaveOccurred())
552
553 tcs = append(tcs, TestCase{
554 name: "filter n2 and n3",
555 pods: pods,
556 filterSelector: n2AndN3Selector,
557 filteredPods: []v1.Pod{pods[1], pods[2]},
558 })
559
560 n2AndN4Selector, err := parseSelector("n2,n4")
561 g.Expect(err).ShouldNot(HaveOccurred())
562
563 tcs = append(tcs, TestCase{
564 name: "filter n2 and n4",
565 pods: pods,
566 filterSelector: n2AndN4Selector,
567 filteredPods: []v1.Pod{pods[1], pods[2], pods[3]},
568 })
569
570 for _, tc := range tcs {
571 g.Expect(filterByNamespaceSelector(tc.pods, tc.filterSelector)).To(Equal(tc.filteredPods), tc.name)
572 }
573 }
574
575 func TestFilterPodByNode(t *testing.T) {
576 g := NewGomegaWithT(t)
577
578 type TestCase struct {
579 name string
580 pods []v1.Pod
581 nodes []v1.Node
582 filteredPods []v1.Pod
583 }
584
585 var tcs []TestCase
586
587 pods := []v1.Pod{
588 NewPod(PodArg{Name: "p1", Namespace: "n1", Nodename: "node1"}),
589 NewPod(PodArg{Name: "p2", Namespace: "n2", Nodename: "node1"}),
590 NewPod(PodArg{Name: "p3", Namespace: "n2", Nodename: "node2"}),
591 NewPod(PodArg{Name: "p4", Namespace: "n4", Nodename: "node3"}),
592 }
593
594 nodes := []v1.Node{
595 NewNode("node1", map[string]string{"disktype": "ssd", "zone": "az1"}),
596 NewNode("node2", map[string]string{"disktype": "hdd", "zone": "az1"}),
597 }
598
599 tcs = append(tcs, TestCase{
600 name: "filter pods from node1 and node2",
601 pods: pods,
602 nodes: nodes,
603 filteredPods: []v1.Pod{pods[0], pods[1], pods[2]},
604 })
605
606 tcs = append(tcs, TestCase{
607 name: "filter no nodes",
608 pods: pods,
609 nodes: []v1.Node{},
610 filteredPods: nil,
611 })
612
613 for _, tc := range tcs {
614 g.Expect(filterPodByNode(tc.pods, tc.nodes)).To(Equal(tc.filteredPods), tc.name)
615 }
616
617 }
618