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