1
16
17 package debug
18
19 import (
20 "fmt"
21 "strings"
22 "testing"
23 "time"
24
25 cmdutil "k8s.io/kubectl/pkg/cmd/util"
26
27 "github.com/google/go-cmp/cmp"
28 "github.com/google/go-cmp/cmp/cmpopts"
29 "github.com/spf13/cobra"
30
31 corev1 "k8s.io/api/core/v1"
32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/cli-runtime/pkg/genericiooptions"
34 cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
35 "k8s.io/utils/pointer"
36 )
37
38 func TestGenerateDebugContainer(t *testing.T) {
39
40 defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
41 var suffixCounter int
42 nameSuffixFunc = func(int) string {
43 suffixCounter++
44 return fmt.Sprint(suffixCounter)
45 }
46
47 for _, tc := range []struct {
48 name string
49 opts *DebugOptions
50 pod *corev1.Pod
51 expected *corev1.EphemeralContainer
52 }{
53 {
54 name: "minimum fields",
55 opts: &DebugOptions{
56 Container: "debugger",
57 Image: "busybox",
58 PullPolicy: corev1.PullIfNotPresent,
59 Profile: ProfileLegacy,
60 },
61 expected: &corev1.EphemeralContainer{
62 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
63 Name: "debugger",
64 Image: "busybox",
65 ImagePullPolicy: "IfNotPresent",
66 TerminationMessagePolicy: "File",
67 },
68 },
69 },
70 {
71 name: "namespace targeting",
72 opts: &DebugOptions{
73 Container: "debugger",
74 Image: "busybox",
75 PullPolicy: corev1.PullIfNotPresent,
76 TargetContainer: "myapp",
77 Profile: ProfileLegacy,
78 },
79 expected: &corev1.EphemeralContainer{
80 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
81 Name: "debugger",
82 Image: "busybox",
83 ImagePullPolicy: "IfNotPresent",
84 TerminationMessagePolicy: "File",
85 },
86 TargetContainerName: "myapp",
87 },
88 },
89 {
90 name: "debug args as container command",
91 opts: &DebugOptions{
92 Args: []string{"/bin/echo", "one", "two", "three"},
93 Container: "debugger",
94 Image: "busybox",
95 PullPolicy: corev1.PullIfNotPresent,
96 Profile: ProfileLegacy,
97 },
98 expected: &corev1.EphemeralContainer{
99 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
100 Name: "debugger",
101 Command: []string{"/bin/echo", "one", "two", "three"},
102 Image: "busybox",
103 ImagePullPolicy: "IfNotPresent",
104 TerminationMessagePolicy: "File",
105 },
106 },
107 },
108 {
109 name: "debug args as container args",
110 opts: &DebugOptions{
111 ArgsOnly: true,
112 Container: "debugger",
113 Args: []string{"echo", "one", "two", "three"},
114 Image: "busybox",
115 PullPolicy: corev1.PullIfNotPresent,
116 Profile: ProfileLegacy,
117 },
118 expected: &corev1.EphemeralContainer{
119 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
120 Name: "debugger",
121 Args: []string{"echo", "one", "two", "three"},
122 Image: "busybox",
123 ImagePullPolicy: "IfNotPresent",
124 TerminationMessagePolicy: "File",
125 },
126 },
127 },
128 {
129 name: "random name generation",
130 opts: &DebugOptions{
131 Image: "busybox",
132 PullPolicy: corev1.PullIfNotPresent,
133 Profile: ProfileLegacy,
134 },
135 expected: &corev1.EphemeralContainer{
136 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
137 Name: "debugger-1",
138 Image: "busybox",
139 ImagePullPolicy: "IfNotPresent",
140 TerminationMessagePolicy: "File",
141 },
142 },
143 },
144 {
145 name: "random name collision",
146 opts: &DebugOptions{
147 Image: "busybox",
148 PullPolicy: corev1.PullIfNotPresent,
149 Profile: ProfileLegacy,
150 },
151 pod: &corev1.Pod{
152 Spec: corev1.PodSpec{
153 Containers: []corev1.Container{
154 {
155 Name: "debugger-1",
156 },
157 },
158 },
159 },
160 expected: &corev1.EphemeralContainer{
161 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
162 Name: "debugger-2",
163 Image: "busybox",
164 ImagePullPolicy: "IfNotPresent",
165 TerminationMessagePolicy: "File",
166 },
167 },
168 },
169 {
170 name: "pod with init containers",
171 opts: &DebugOptions{
172 Image: "busybox",
173 PullPolicy: corev1.PullIfNotPresent,
174 Profile: ProfileLegacy,
175 },
176 pod: &corev1.Pod{
177 Spec: corev1.PodSpec{
178 InitContainers: []corev1.Container{
179 {
180 Name: "init-container-1",
181 },
182 {
183 Name: "init-container-2",
184 },
185 },
186 Containers: []corev1.Container{
187 {
188 Name: "debugger",
189 },
190 },
191 },
192 },
193 expected: &corev1.EphemeralContainer{
194 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
195 Name: "debugger-1",
196 Image: "busybox",
197 ImagePullPolicy: "IfNotPresent",
198 TerminationMessagePolicy: "File",
199 },
200 },
201 },
202 {
203 name: "pod with ephemeral containers",
204 opts: &DebugOptions{
205 Image: "busybox",
206 PullPolicy: corev1.PullIfNotPresent,
207 Profile: ProfileLegacy,
208 },
209 pod: &corev1.Pod{
210 Spec: corev1.PodSpec{
211 Containers: []corev1.Container{
212 {
213 Name: "debugger",
214 },
215 },
216 EphemeralContainers: []corev1.EphemeralContainer{
217 {
218 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
219 Name: "ephemeral-container-1",
220 },
221 },
222 {
223 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
224 Name: "ephemeral-container-2",
225 },
226 },
227 },
228 },
229 },
230 expected: &corev1.EphemeralContainer{
231 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
232 Name: "debugger-1",
233 Image: "busybox",
234 ImagePullPolicy: "IfNotPresent",
235 TerminationMessagePolicy: "File",
236 },
237 },
238 },
239 {
240 name: "general profile",
241 opts: &DebugOptions{
242 Image: "busybox",
243 PullPolicy: corev1.PullIfNotPresent,
244 Profile: ProfileGeneral,
245 },
246 expected: &corev1.EphemeralContainer{
247 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
248 Name: "debugger-1",
249 Image: "busybox",
250 ImagePullPolicy: corev1.PullIfNotPresent,
251 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
252 SecurityContext: &corev1.SecurityContext{
253 Capabilities: &corev1.Capabilities{
254 Add: []corev1.Capability{"SYS_PTRACE"},
255 },
256 },
257 },
258 },
259 },
260 {
261 name: "baseline profile",
262 opts: &DebugOptions{
263 Image: "busybox",
264 PullPolicy: corev1.PullIfNotPresent,
265 Profile: ProfileBaseline,
266 },
267 expected: &corev1.EphemeralContainer{
268 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
269 Name: "debugger-1",
270 Image: "busybox",
271 ImagePullPolicy: corev1.PullIfNotPresent,
272 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
273 },
274 },
275 },
276 {
277 name: "restricted profile",
278 opts: &DebugOptions{
279 Image: "busybox",
280 PullPolicy: corev1.PullIfNotPresent,
281 Profile: ProfileRestricted,
282 },
283 expected: &corev1.EphemeralContainer{
284 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
285 Name: "debugger-1",
286 Image: "busybox",
287 ImagePullPolicy: corev1.PullIfNotPresent,
288 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
289 SecurityContext: &corev1.SecurityContext{
290 RunAsNonRoot: pointer.Bool(true),
291 Capabilities: &corev1.Capabilities{
292 Drop: []corev1.Capability{"ALL"},
293 },
294 AllowPrivilegeEscalation: pointer.Bool(false),
295 SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
296 },
297 },
298 },
299 },
300 {
301 name: "netadmin profile",
302 opts: &DebugOptions{
303 Image: "busybox",
304 PullPolicy: corev1.PullIfNotPresent,
305 Profile: ProfileNetadmin,
306 },
307 expected: &corev1.EphemeralContainer{
308 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
309 Name: "debugger-1",
310 Image: "busybox",
311 ImagePullPolicy: corev1.PullIfNotPresent,
312 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
313 SecurityContext: &corev1.SecurityContext{
314 Capabilities: &corev1.Capabilities{
315 Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
316 },
317 },
318 },
319 },
320 },
321 {
322 name: "sysadmin profile",
323 opts: &DebugOptions{
324 Image: "busybox",
325 PullPolicy: corev1.PullIfNotPresent,
326 Profile: ProfileSysadmin,
327 },
328 expected: &corev1.EphemeralContainer{
329 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
330 Name: "debugger-1",
331 Image: "busybox",
332 ImagePullPolicy: corev1.PullIfNotPresent,
333 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
334 SecurityContext: &corev1.SecurityContext{
335 Privileged: pointer.Bool(true),
336 },
337 },
338 },
339 },
340 } {
341 t.Run(tc.name, func(t *testing.T) {
342 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
343 suffixCounter = 0
344
345 if tc.pod == nil {
346 tc.pod = &corev1.Pod{}
347 }
348
349 applier, err := NewProfileApplier(tc.opts.Profile)
350 if err != nil {
351 t.Fatalf("failed to create profile applier: %s: %v", tc.opts.Profile, err)
352 }
353 tc.opts.Applier = applier
354
355 _, debugContainer, err := tc.opts.generateDebugContainer(tc.pod)
356 if err != nil {
357 t.Fatalf("fail to generate debug container: %v", err)
358 }
359 if diff := cmp.Diff(tc.expected, debugContainer); diff != "" {
360 t.Error("unexpected diff in generated object: (-want +got):\n", diff)
361 }
362 })
363 }
364 }
365
366 func TestGeneratePodCopyWithDebugContainer(t *testing.T) {
367 defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
368 var suffixCounter int
369 nameSuffixFunc = func(int) string {
370 suffixCounter++
371 return fmt.Sprint(suffixCounter)
372 }
373
374 for _, tc := range []struct {
375 name string
376 opts *DebugOptions
377 havePod, wantPod *corev1.Pod
378 }{
379 {
380 name: "basic",
381 opts: &DebugOptions{
382 CopyTo: "debugger",
383 Container: "debugger",
384 Image: "busybox",
385 PullPolicy: corev1.PullIfNotPresent,
386 Profile: ProfileLegacy,
387 },
388 havePod: &corev1.Pod{
389 ObjectMeta: metav1.ObjectMeta{
390 Name: "target",
391 },
392 Spec: corev1.PodSpec{
393 Containers: []corev1.Container{
394 {
395 Name: "debugger",
396 },
397 },
398 NodeName: "node-1",
399 },
400 },
401 wantPod: &corev1.Pod{
402 ObjectMeta: metav1.ObjectMeta{
403 Name: "debugger",
404 },
405 Spec: corev1.PodSpec{
406 Containers: []corev1.Container{
407 {
408 Name: "debugger",
409 Image: "busybox",
410 ImagePullPolicy: corev1.PullIfNotPresent,
411 },
412 },
413 },
414 },
415 },
416 {
417 name: "same node",
418 opts: &DebugOptions{
419 CopyTo: "debugger",
420 Container: "debugger",
421 Image: "busybox",
422 PullPolicy: corev1.PullIfNotPresent,
423 SameNode: true,
424 Profile: ProfileLegacy,
425 },
426 havePod: &corev1.Pod{
427 ObjectMeta: metav1.ObjectMeta{
428 Name: "target",
429 },
430 Spec: corev1.PodSpec{
431 Containers: []corev1.Container{
432 {
433 Name: "debugger",
434 },
435 },
436 NodeName: "node-1",
437 },
438 },
439 wantPod: &corev1.Pod{
440 ObjectMeta: metav1.ObjectMeta{
441 Name: "debugger",
442 },
443 Spec: corev1.PodSpec{
444 Containers: []corev1.Container{
445 {
446 Name: "debugger",
447 Image: "busybox",
448 ImagePullPolicy: corev1.PullIfNotPresent,
449 },
450 },
451 NodeName: "node-1",
452 },
453 },
454 },
455 {
456 name: "metadata stripping",
457 opts: &DebugOptions{
458 CopyTo: "debugger",
459 Container: "debugger",
460 Image: "busybox",
461 PullPolicy: corev1.PullIfNotPresent,
462 Profile: ProfileLegacy,
463 },
464 havePod: &corev1.Pod{
465 ObjectMeta: metav1.ObjectMeta{
466 Name: "target",
467 Labels: map[string]string{
468 "app": "business",
469 },
470 Annotations: map[string]string{
471 "test": "test",
472 },
473 ResourceVersion: "1",
474 CreationTimestamp: metav1.Time{Time: time.Now()},
475 },
476 Spec: corev1.PodSpec{
477 Containers: []corev1.Container{
478 {
479 Name: "debugger",
480 },
481 },
482 },
483 },
484 wantPod: &corev1.Pod{
485 ObjectMeta: metav1.ObjectMeta{
486 Name: "debugger",
487 Annotations: map[string]string{
488 "test": "test",
489 },
490 },
491 Spec: corev1.PodSpec{
492 Containers: []corev1.Container{
493 {
494 Name: "debugger",
495 Image: "busybox",
496 ImagePullPolicy: corev1.PullIfNotPresent,
497 },
498 },
499 },
500 },
501 },
502 {
503 name: "add a debug container",
504 opts: &DebugOptions{
505 CopyTo: "debugger",
506 Container: "debugger",
507 Image: "busybox",
508 PullPolicy: corev1.PullIfNotPresent,
509 Profile: ProfileLegacy,
510 },
511 havePod: &corev1.Pod{
512 ObjectMeta: metav1.ObjectMeta{
513 Name: "target",
514 },
515 Spec: corev1.PodSpec{
516 Containers: []corev1.Container{
517 {
518 Name: "business",
519 },
520 },
521 },
522 },
523 wantPod: &corev1.Pod{
524 ObjectMeta: metav1.ObjectMeta{
525 Name: "debugger",
526 },
527 Spec: corev1.PodSpec{
528 Containers: []corev1.Container{
529 {
530 Name: "business",
531 },
532 {
533 Name: "debugger",
534 Image: "busybox",
535 ImagePullPolicy: corev1.PullIfNotPresent,
536 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
537 },
538 },
539 },
540 },
541 },
542 {
543 name: "customize envs",
544 opts: &DebugOptions{
545 CopyTo: "debugger",
546 Container: "debugger",
547 Image: "busybox",
548 PullPolicy: corev1.PullIfNotPresent,
549 Env: []corev1.EnvVar{{
550 Name: "TEST",
551 Value: "test",
552 }},
553 Profile: ProfileLegacy,
554 },
555 havePod: &corev1.Pod{
556 ObjectMeta: metav1.ObjectMeta{
557 Name: "target",
558 },
559 Spec: corev1.PodSpec{
560 Containers: []corev1.Container{
561 {
562 Name: "business",
563 },
564 },
565 },
566 },
567 wantPod: &corev1.Pod{
568 ObjectMeta: metav1.ObjectMeta{
569 Name: "debugger",
570 },
571 Spec: corev1.PodSpec{
572 Containers: []corev1.Container{
573 {
574 Name: "business",
575 },
576 {
577 Name: "debugger",
578 Image: "busybox",
579 ImagePullPolicy: corev1.PullIfNotPresent,
580 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
581 Env: []corev1.EnvVar{{
582 Name: "TEST",
583 Value: "test",
584 }},
585 },
586 },
587 },
588 },
589 },
590 {
591 name: "debug args as container command",
592 opts: &DebugOptions{
593 CopyTo: "debugger",
594 Container: "debugger",
595 Args: []string{"/bin/echo", "one", "two", "three"},
596 Image: "busybox",
597 PullPolicy: corev1.PullIfNotPresent,
598 Profile: ProfileLegacy,
599 },
600 havePod: &corev1.Pod{
601 ObjectMeta: metav1.ObjectMeta{
602 Name: "target",
603 },
604 Spec: corev1.PodSpec{
605 Containers: []corev1.Container{
606 {
607 Name: "business",
608 },
609 },
610 },
611 },
612 wantPod: &corev1.Pod{
613 ObjectMeta: metav1.ObjectMeta{
614 Name: "debugger",
615 },
616 Spec: corev1.PodSpec{
617 Containers: []corev1.Container{
618 {
619 Name: "business",
620 },
621 {
622 Name: "debugger",
623 Image: "busybox",
624 Command: []string{"/bin/echo", "one", "two", "three"},
625 ImagePullPolicy: corev1.PullIfNotPresent,
626 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
627 },
628 },
629 },
630 },
631 },
632 {
633 name: "debug args as container command",
634 opts: &DebugOptions{
635 CopyTo: "debugger",
636 Container: "debugger",
637 Args: []string{"one", "two", "three"},
638 ArgsOnly: true,
639 Image: "busybox",
640 PullPolicy: corev1.PullIfNotPresent,
641 Profile: ProfileLegacy,
642 },
643 havePod: &corev1.Pod{
644 ObjectMeta: metav1.ObjectMeta{
645 Name: "target",
646 },
647 Spec: corev1.PodSpec{
648 Containers: []corev1.Container{
649 {
650 Name: "business",
651 },
652 },
653 },
654 },
655 wantPod: &corev1.Pod{
656 ObjectMeta: metav1.ObjectMeta{
657 Name: "debugger",
658 },
659 Spec: corev1.PodSpec{
660 Containers: []corev1.Container{
661 {
662 Name: "business",
663 },
664 {
665 Name: "debugger",
666 Image: "busybox",
667 Args: []string{"one", "two", "three"},
668 ImagePullPolicy: corev1.PullIfNotPresent,
669 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
670 },
671 },
672 },
673 },
674 },
675 {
676 name: "modify existing command to debug args",
677 opts: &DebugOptions{
678 CopyTo: "debugger",
679 Container: "debugger",
680 Args: []string{"sleep", "1d"},
681 PullPolicy: corev1.PullIfNotPresent,
682 Profile: ProfileLegacy,
683 },
684 havePod: &corev1.Pod{
685 ObjectMeta: metav1.ObjectMeta{
686 Name: "target",
687 },
688 Spec: corev1.PodSpec{
689 Containers: []corev1.Container{
690 {
691 Name: "debugger",
692 Command: []string{"echo"},
693 Image: "app",
694 Args: []string{"one", "two", "three"},
695 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
696 },
697 },
698 },
699 },
700 wantPod: &corev1.Pod{
701 ObjectMeta: metav1.ObjectMeta{
702 Name: "debugger",
703 },
704 Spec: corev1.PodSpec{
705 Containers: []corev1.Container{
706 {
707 Name: "debugger",
708 Image: "app",
709 Command: []string{"sleep", "1d"},
710 ImagePullPolicy: corev1.PullIfNotPresent,
711 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
712 },
713 },
714 },
715 },
716 },
717 {
718 name: "random name",
719 opts: &DebugOptions{
720 CopyTo: "debugger",
721 Image: "busybox",
722 PullPolicy: corev1.PullIfNotPresent,
723 Profile: ProfileLegacy,
724 },
725 havePod: &corev1.Pod{
726 ObjectMeta: metav1.ObjectMeta{
727 Name: "target",
728 },
729 Spec: corev1.PodSpec{
730 Containers: []corev1.Container{
731 {
732 Name: "business",
733 },
734 },
735 },
736 },
737 wantPod: &corev1.Pod{
738 ObjectMeta: metav1.ObjectMeta{
739 Name: "debugger",
740 },
741 Spec: corev1.PodSpec{
742 Containers: []corev1.Container{
743 {
744 Name: "business",
745 },
746 {
747 Name: "debugger-1",
748 Image: "busybox",
749 ImagePullPolicy: corev1.PullIfNotPresent,
750 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
751 },
752 },
753 },
754 },
755 },
756 {
757 name: "random name collision",
758 opts: &DebugOptions{
759 CopyTo: "debugger",
760 Image: "busybox",
761 PullPolicy: corev1.PullIfNotPresent,
762 Profile: ProfileLegacy,
763 },
764 havePod: &corev1.Pod{
765 ObjectMeta: metav1.ObjectMeta{
766 Name: "target",
767 },
768 Spec: corev1.PodSpec{
769 Containers: []corev1.Container{
770 {
771 Name: "debugger-1",
772 },
773 },
774 },
775 },
776 wantPod: &corev1.Pod{
777 ObjectMeta: metav1.ObjectMeta{
778 Name: "debugger",
779 },
780 Spec: corev1.PodSpec{
781 Containers: []corev1.Container{
782 {
783 Name: "debugger-1",
784 },
785 {
786 Name: "debugger-2",
787 Image: "busybox",
788 ImagePullPolicy: corev1.PullIfNotPresent,
789 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
790 },
791 },
792 },
793 },
794 },
795 {
796 name: "pod with init containers",
797 opts: &DebugOptions{
798 CopyTo: "debugger",
799 Image: "busybox",
800 PullPolicy: corev1.PullIfNotPresent,
801 Profile: ProfileLegacy,
802 },
803 havePod: &corev1.Pod{
804 ObjectMeta: metav1.ObjectMeta{
805 Name: "target",
806 },
807 Spec: corev1.PodSpec{
808 InitContainers: []corev1.Container{
809 {
810 Name: "init-container-1",
811 },
812 {
813 Name: "init-container-2",
814 },
815 },
816 Containers: []corev1.Container{
817 {
818 Name: "debugger-1",
819 },
820 },
821 },
822 },
823 wantPod: &corev1.Pod{
824 ObjectMeta: metav1.ObjectMeta{
825 Name: "debugger",
826 },
827 Spec: corev1.PodSpec{
828 InitContainers: []corev1.Container{
829 {
830 Name: "init-container-1",
831 },
832 {
833 Name: "init-container-2",
834 },
835 },
836 Containers: []corev1.Container{
837 {
838 Name: "debugger-1",
839 },
840 {
841 Name: "debugger-2",
842 Image: "busybox",
843 ImagePullPolicy: corev1.PullIfNotPresent,
844 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
845 },
846 },
847 },
848 },
849 },
850 {
851 name: "pod with ephemeral containers",
852 opts: &DebugOptions{
853 CopyTo: "debugger",
854 Image: "busybox",
855 PullPolicy: corev1.PullIfNotPresent,
856 Profile: ProfileLegacy,
857 },
858 havePod: &corev1.Pod{
859 ObjectMeta: metav1.ObjectMeta{
860 Name: "target",
861 },
862 Spec: corev1.PodSpec{
863 Containers: []corev1.Container{
864 {
865 Name: "debugger-1",
866 },
867 },
868 EphemeralContainers: []corev1.EphemeralContainer{
869 {
870 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
871 Name: "ephemeral-container-1",
872 },
873 },
874 {
875 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
876 Name: "ephemeral-container-2",
877 },
878 },
879 },
880 },
881 },
882 wantPod: &corev1.Pod{
883 ObjectMeta: metav1.ObjectMeta{
884 Name: "debugger",
885 },
886 Spec: corev1.PodSpec{
887 Containers: []corev1.Container{
888 {
889 Name: "debugger-1",
890 },
891 {
892 Name: "debugger-2",
893 Image: "busybox",
894 ImagePullPolicy: corev1.PullIfNotPresent,
895 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
896 },
897 },
898 },
899 },
900 },
901 {
902 name: "shared process namespace",
903 opts: &DebugOptions{
904 CopyTo: "debugger",
905 Container: "debugger",
906 Image: "busybox",
907 PullPolicy: corev1.PullIfNotPresent,
908 ShareProcesses: true,
909 shareProcessedChanged: true,
910 Profile: ProfileLegacy,
911 },
912 havePod: &corev1.Pod{
913 ObjectMeta: metav1.ObjectMeta{
914 Name: "target",
915 },
916 Spec: corev1.PodSpec{
917 Containers: []corev1.Container{
918 {
919 Name: "debugger",
920 ImagePullPolicy: corev1.PullAlways,
921 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
922 },
923 },
924 NodeName: "node-1",
925 },
926 },
927 wantPod: &corev1.Pod{
928 ObjectMeta: metav1.ObjectMeta{
929 Name: "debugger",
930 },
931 Spec: corev1.PodSpec{
932 Containers: []corev1.Container{
933 {
934 Name: "debugger",
935 Image: "busybox",
936 ImagePullPolicy: corev1.PullIfNotPresent,
937 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
938 },
939 },
940 ShareProcessNamespace: pointer.Bool(true),
941 },
942 },
943 },
944 {
945 name: "Change image for a named container",
946 opts: &DebugOptions{
947 Args: []string{},
948 CopyTo: "myapp-copy",
949 Container: "app",
950 Image: "busybox",
951 TargetNames: []string{"myapp"},
952 Profile: ProfileLegacy,
953 },
954 havePod: &corev1.Pod{
955 ObjectMeta: metav1.ObjectMeta{Name: "myapp"},
956 Spec: corev1.PodSpec{
957 Containers: []corev1.Container{
958 {Name: "app", Image: "appimage"},
959 {Name: "sidecar", Image: "sidecarimage"},
960 },
961 },
962 },
963 wantPod: &corev1.Pod{
964 ObjectMeta: metav1.ObjectMeta{Name: "myapp-copy"},
965 Spec: corev1.PodSpec{
966 Containers: []corev1.Container{
967 {Name: "app", Image: "busybox"},
968 {Name: "sidecar", Image: "sidecarimage"},
969 },
970 },
971 },
972 },
973 {
974 name: "Change image for a named container with set-image",
975 opts: &DebugOptions{
976 CopyTo: "myapp-copy",
977 Container: "app",
978 SetImages: map[string]string{"app": "busybox"},
979 Profile: ProfileLegacy,
980 },
981 havePod: &corev1.Pod{
982 ObjectMeta: metav1.ObjectMeta{
983 Name: "myapp",
984 },
985 Spec: corev1.PodSpec{
986 Containers: []corev1.Container{
987 {Name: "app", Image: "appimage"},
988 {Name: "sidecar", Image: "sidecarimage"},
989 },
990 },
991 },
992 wantPod: &corev1.Pod{
993 ObjectMeta: metav1.ObjectMeta{
994 Name: "myapp-copy",
995 },
996 Spec: corev1.PodSpec{
997 Containers: []corev1.Container{
998 {Name: "app", Image: "busybox"},
999 {Name: "sidecar", Image: "sidecarimage"},
1000 },
1001 },
1002 },
1003 },
1004 {
1005 name: "Change image for all containers with set-image",
1006 opts: &DebugOptions{
1007 CopyTo: "myapp-copy",
1008 SetImages: map[string]string{"*": "busybox"},
1009 Profile: ProfileLegacy,
1010 },
1011 havePod: &corev1.Pod{
1012 ObjectMeta: metav1.ObjectMeta{
1013 Name: "myapp",
1014 },
1015 Spec: corev1.PodSpec{
1016 Containers: []corev1.Container{
1017 {Name: "app", Image: "appimage"},
1018 {Name: "sidecar", Image: "sidecarimage"},
1019 },
1020 },
1021 },
1022 wantPod: &corev1.Pod{
1023 ObjectMeta: metav1.ObjectMeta{
1024 Name: "myapp-copy",
1025 },
1026 Spec: corev1.PodSpec{
1027 Containers: []corev1.Container{
1028 {Name: "app", Image: "busybox"},
1029 {Name: "sidecar", Image: "busybox"},
1030 },
1031 },
1032 },
1033 },
1034 {
1035 name: "Change image for multiple containers with set-image",
1036 opts: &DebugOptions{
1037 CopyTo: "myapp-copy",
1038 SetImages: map[string]string{"*": "busybox", "app": "app-debugger"},
1039 Profile: ProfileLegacy,
1040 },
1041 havePod: &corev1.Pod{
1042 ObjectMeta: metav1.ObjectMeta{
1043 Name: "myapp",
1044 },
1045 Spec: corev1.PodSpec{
1046 Containers: []corev1.Container{
1047 {Name: "app", Image: "appimage"},
1048 {Name: "sidecar", Image: "sidecarimage"},
1049 },
1050 },
1051 },
1052 wantPod: &corev1.Pod{
1053 ObjectMeta: metav1.ObjectMeta{
1054 Name: "myapp-copy",
1055 },
1056 Spec: corev1.PodSpec{
1057 Containers: []corev1.Container{
1058 {Name: "app", Image: "app-debugger"},
1059 {Name: "sidecar", Image: "busybox"},
1060 },
1061 },
1062 },
1063 },
1064 {
1065 name: "Add interactive debug container minimal args",
1066 opts: &DebugOptions{
1067 Args: []string{},
1068 Attach: true,
1069 CopyTo: "my-debugger",
1070 Image: "busybox",
1071 Interactive: true,
1072 TargetNames: []string{"mypod"},
1073 TTY: true,
1074 Profile: ProfileLegacy,
1075 },
1076 havePod: &corev1.Pod{
1077 ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
1078 Spec: corev1.PodSpec{
1079 Containers: []corev1.Container{
1080 {Name: "app", Image: "appimage"},
1081 {Name: "sidecar", Image: "sidecarimage"},
1082 },
1083 },
1084 },
1085 wantPod: &corev1.Pod{
1086 ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
1087 Spec: corev1.PodSpec{
1088 Containers: []corev1.Container{
1089 {Name: "app", Image: "appimage"},
1090 {Name: "sidecar", Image: "sidecarimage"},
1091 {
1092 Name: "debugger-1",
1093 Image: "busybox",
1094 Stdin: true,
1095 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1096 TTY: true,
1097 },
1098 },
1099 },
1100 },
1101 },
1102 {
1103 name: "Pod copy: add container and also mutate images",
1104 opts: &DebugOptions{
1105 Args: []string{},
1106 Attach: true,
1107 CopyTo: "my-debugger",
1108 Image: "debian",
1109 Interactive: true,
1110 Namespace: "default",
1111 SetImages: map[string]string{
1112 "app": "app:debug",
1113 "sidecar": "sidecar:debug",
1114 },
1115 ShareProcesses: true,
1116 TargetNames: []string{"mypod"},
1117 TTY: true,
1118 Profile: ProfileLegacy,
1119 },
1120 havePod: &corev1.Pod{
1121 ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
1122 Spec: corev1.PodSpec{
1123 Containers: []corev1.Container{
1124 {Name: "app", Image: "appimage"},
1125 {Name: "sidecar", Image: "sidecarimage"},
1126 },
1127 },
1128 },
1129 wantPod: &corev1.Pod{
1130 ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
1131 Spec: corev1.PodSpec{
1132 Containers: []corev1.Container{
1133 {Name: "app", Image: "app:debug"},
1134 {Name: "sidecar", Image: "sidecar:debug"},
1135 {
1136 Name: "debugger-1",
1137 Image: "debian",
1138 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1139 Stdin: true,
1140 TTY: true,
1141 },
1142 },
1143 },
1144 },
1145 },
1146 {
1147 name: "general profile",
1148 opts: &DebugOptions{
1149 CopyTo: "debugger",
1150 Container: "debugger",
1151 Image: "busybox",
1152 PullPolicy: corev1.PullIfNotPresent,
1153 Profile: ProfileGeneral,
1154 },
1155 havePod: &corev1.Pod{
1156 ObjectMeta: metav1.ObjectMeta{
1157 Name: "target",
1158 },
1159 Spec: corev1.PodSpec{
1160 Containers: []corev1.Container{
1161 {
1162 Name: "debugger",
1163 },
1164 },
1165 NodeName: "node-1",
1166 },
1167 },
1168 wantPod: &corev1.Pod{
1169 ObjectMeta: metav1.ObjectMeta{
1170 Name: "debugger",
1171 },
1172 Spec: corev1.PodSpec{
1173 Containers: []corev1.Container{
1174 {
1175 Name: "debugger",
1176 Image: "busybox",
1177 ImagePullPolicy: corev1.PullIfNotPresent,
1178 SecurityContext: &corev1.SecurityContext{
1179 Capabilities: &corev1.Capabilities{
1180 Add: []corev1.Capability{"SYS_PTRACE"},
1181 },
1182 },
1183 },
1184 },
1185 ShareProcessNamespace: pointer.Bool(true),
1186 },
1187 },
1188 },
1189 {
1190 name: "baseline profile",
1191 opts: &DebugOptions{
1192 CopyTo: "debugger",
1193 Container: "debugger",
1194 Image: "busybox",
1195 PullPolicy: corev1.PullIfNotPresent,
1196 Profile: ProfileBaseline,
1197 },
1198 havePod: &corev1.Pod{
1199 ObjectMeta: metav1.ObjectMeta{
1200 Name: "target",
1201 },
1202 Spec: corev1.PodSpec{
1203 Containers: []corev1.Container{
1204 {
1205 Name: "debugger",
1206 },
1207 },
1208 NodeName: "node-1",
1209 },
1210 },
1211 wantPod: &corev1.Pod{
1212 ObjectMeta: metav1.ObjectMeta{
1213 Name: "debugger",
1214 },
1215 Spec: corev1.PodSpec{
1216 Containers: []corev1.Container{
1217 {
1218 Name: "debugger",
1219 Image: "busybox",
1220 ImagePullPolicy: corev1.PullIfNotPresent,
1221 },
1222 },
1223 ShareProcessNamespace: pointer.Bool(true),
1224 },
1225 },
1226 },
1227 {
1228 name: "baseline profile not share process when user explicitly disables it",
1229 opts: &DebugOptions{
1230 CopyTo: "debugger",
1231 Container: "debugger",
1232 Image: "busybox",
1233 PullPolicy: corev1.PullIfNotPresent,
1234 Profile: ProfileBaseline,
1235 ShareProcesses: false,
1236 shareProcessedChanged: true,
1237 },
1238 havePod: &corev1.Pod{
1239 ObjectMeta: metav1.ObjectMeta{
1240 Name: "target",
1241 },
1242 Spec: corev1.PodSpec{
1243 Containers: []corev1.Container{
1244 {
1245 Name: "debugger",
1246 },
1247 },
1248 NodeName: "node-1",
1249 },
1250 },
1251 wantPod: &corev1.Pod{
1252 ObjectMeta: metav1.ObjectMeta{
1253 Name: "debugger",
1254 },
1255 Spec: corev1.PodSpec{
1256 Containers: []corev1.Container{
1257 {
1258 Name: "debugger",
1259 Image: "busybox",
1260 ImagePullPolicy: corev1.PullIfNotPresent,
1261 },
1262 },
1263 ShareProcessNamespace: pointer.Bool(false),
1264 },
1265 },
1266 },
1267 {
1268 name: "restricted profile",
1269 opts: &DebugOptions{
1270 CopyTo: "debugger",
1271 Container: "debugger",
1272 Image: "busybox",
1273 PullPolicy: corev1.PullIfNotPresent,
1274 Profile: ProfileRestricted,
1275 },
1276 havePod: &corev1.Pod{
1277 ObjectMeta: metav1.ObjectMeta{
1278 Name: "target",
1279 },
1280 Spec: corev1.PodSpec{
1281 Containers: []corev1.Container{
1282 {
1283 Name: "debugger",
1284 },
1285 },
1286 NodeName: "node-1",
1287 },
1288 },
1289 wantPod: &corev1.Pod{
1290 ObjectMeta: metav1.ObjectMeta{
1291 Name: "debugger",
1292 },
1293 Spec: corev1.PodSpec{
1294 Containers: []corev1.Container{
1295 {
1296 Name: "debugger",
1297 Image: "busybox",
1298 ImagePullPolicy: corev1.PullIfNotPresent,
1299 SecurityContext: &corev1.SecurityContext{
1300 RunAsNonRoot: pointer.Bool(true),
1301 Capabilities: &corev1.Capabilities{
1302 Drop: []corev1.Capability{"ALL"},
1303 },
1304 AllowPrivilegeEscalation: pointer.Bool(false),
1305 SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
1306 },
1307 },
1308 },
1309 ShareProcessNamespace: pointer.Bool(true),
1310 },
1311 },
1312 },
1313 {
1314 name: "netadmin profile",
1315 opts: &DebugOptions{
1316 CopyTo: "debugger",
1317 Container: "debugger",
1318 Image: "busybox",
1319 PullPolicy: corev1.PullIfNotPresent,
1320 Profile: ProfileNetadmin,
1321 },
1322 havePod: &corev1.Pod{
1323 ObjectMeta: metav1.ObjectMeta{
1324 Name: "target",
1325 },
1326 Spec: corev1.PodSpec{
1327 Containers: []corev1.Container{
1328 {
1329 Name: "debugger",
1330 },
1331 },
1332 NodeName: "node-1",
1333 },
1334 },
1335 wantPod: &corev1.Pod{
1336 ObjectMeta: metav1.ObjectMeta{
1337 Name: "debugger",
1338 },
1339 Spec: corev1.PodSpec{
1340 Containers: []corev1.Container{
1341 {
1342 Name: "debugger",
1343 Image: "busybox",
1344 ImagePullPolicy: corev1.PullIfNotPresent,
1345 SecurityContext: &corev1.SecurityContext{
1346 Capabilities: &corev1.Capabilities{
1347 Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
1348 },
1349 },
1350 },
1351 },
1352 ShareProcessNamespace: pointer.Bool(true),
1353 },
1354 },
1355 },
1356 } {
1357 t.Run(tc.name, func(t *testing.T) {
1358 var err error
1359 tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
1360 if err != nil {
1361 t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
1362 }
1363 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
1364 suffixCounter = 0
1365
1366 if tc.havePod == nil {
1367 tc.havePod = &corev1.Pod{}
1368 }
1369 gotPod, _, _ := tc.opts.generatePodCopyWithDebugContainer(tc.havePod)
1370 if diff := cmp.Diff(tc.wantPod, gotPod); diff != "" {
1371 t.Error("TestGeneratePodCopyWithDebugContainer: diff in generated object: (-want +got):\n", diff)
1372 }
1373 })
1374 }
1375 }
1376
1377 func TestGenerateNodeDebugPod(t *testing.T) {
1378 defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
1379 var suffixCounter int
1380 nameSuffixFunc = func(int) string {
1381 suffixCounter++
1382 return fmt.Sprint(suffixCounter)
1383 }
1384
1385 for _, tc := range []struct {
1386 name string
1387 node *corev1.Node
1388 opts *DebugOptions
1389 expected *corev1.Pod
1390 }{
1391 {
1392 name: "minimum options",
1393 node: &corev1.Node{
1394 ObjectMeta: metav1.ObjectMeta{
1395 Name: "node-XXX",
1396 },
1397 },
1398 opts: &DebugOptions{
1399 Image: "busybox",
1400 PullPolicy: corev1.PullIfNotPresent,
1401 Profile: ProfileLegacy,
1402 },
1403 expected: &corev1.Pod{
1404 ObjectMeta: metav1.ObjectMeta{
1405 Name: "node-debugger-node-XXX-1",
1406 },
1407 Spec: corev1.PodSpec{
1408 Containers: []corev1.Container{
1409 {
1410 Name: "debugger",
1411 Image: "busybox",
1412 ImagePullPolicy: corev1.PullIfNotPresent,
1413 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1414 VolumeMounts: []corev1.VolumeMount{
1415 {
1416 MountPath: "/host",
1417 Name: "host-root",
1418 },
1419 },
1420 },
1421 },
1422 HostIPC: true,
1423 HostNetwork: true,
1424 HostPID: true,
1425 NodeName: "node-XXX",
1426 RestartPolicy: corev1.RestartPolicyNever,
1427 Volumes: []corev1.Volume{
1428 {
1429 Name: "host-root",
1430 VolumeSource: corev1.VolumeSource{
1431 HostPath: &corev1.HostPathVolumeSource{Path: "/"},
1432 },
1433 },
1434 },
1435 Tolerations: []corev1.Toleration{
1436 {
1437 Operator: corev1.TolerationOpExists,
1438 },
1439 },
1440 },
1441 },
1442 },
1443 {
1444 name: "debug args as container command",
1445 node: &corev1.Node{
1446 ObjectMeta: metav1.ObjectMeta{
1447 Name: "node-XXX",
1448 },
1449 },
1450 opts: &DebugOptions{
1451 Args: []string{"/bin/echo", "one", "two", "three"},
1452 Container: "custom-debugger",
1453 Image: "busybox",
1454 PullPolicy: corev1.PullIfNotPresent,
1455 Profile: ProfileLegacy,
1456 },
1457 expected: &corev1.Pod{
1458 ObjectMeta: metav1.ObjectMeta{
1459 Name: "node-debugger-node-XXX-1",
1460 },
1461 Spec: corev1.PodSpec{
1462 Containers: []corev1.Container{
1463 {
1464 Name: "custom-debugger",
1465 Command: []string{"/bin/echo", "one", "two", "three"},
1466 Image: "busybox",
1467 ImagePullPolicy: corev1.PullIfNotPresent,
1468 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1469 VolumeMounts: []corev1.VolumeMount{
1470 {
1471 MountPath: "/host",
1472 Name: "host-root",
1473 },
1474 },
1475 },
1476 },
1477 HostIPC: true,
1478 HostNetwork: true,
1479 HostPID: true,
1480 NodeName: "node-XXX",
1481 RestartPolicy: corev1.RestartPolicyNever,
1482 Volumes: []corev1.Volume{
1483 {
1484 Name: "host-root",
1485 VolumeSource: corev1.VolumeSource{
1486 HostPath: &corev1.HostPathVolumeSource{Path: "/"},
1487 },
1488 },
1489 },
1490 Tolerations: []corev1.Toleration{
1491 {
1492 Operator: corev1.TolerationOpExists,
1493 },
1494 },
1495 },
1496 },
1497 },
1498 {
1499 name: "debug args as container args",
1500 node: &corev1.Node{
1501 ObjectMeta: metav1.ObjectMeta{
1502 Name: "node-XXX",
1503 },
1504 },
1505 opts: &DebugOptions{
1506 ArgsOnly: true,
1507 Container: "custom-debugger",
1508 Args: []string{"echo", "one", "two", "three"},
1509 Image: "busybox",
1510 PullPolicy: corev1.PullIfNotPresent,
1511 Profile: ProfileLegacy,
1512 },
1513 expected: &corev1.Pod{
1514 ObjectMeta: metav1.ObjectMeta{
1515 Name: "node-debugger-node-XXX-1",
1516 },
1517 Spec: corev1.PodSpec{
1518 Containers: []corev1.Container{
1519 {
1520 Name: "custom-debugger",
1521 Args: []string{"echo", "one", "two", "three"},
1522 Image: "busybox",
1523 ImagePullPolicy: corev1.PullIfNotPresent,
1524 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1525 VolumeMounts: []corev1.VolumeMount{
1526 {
1527 MountPath: "/host",
1528 Name: "host-root",
1529 },
1530 },
1531 },
1532 },
1533 HostIPC: true,
1534 HostNetwork: true,
1535 HostPID: true,
1536 NodeName: "node-XXX",
1537 RestartPolicy: corev1.RestartPolicyNever,
1538 Volumes: []corev1.Volume{
1539 {
1540 Name: "host-root",
1541 VolumeSource: corev1.VolumeSource{
1542 HostPath: &corev1.HostPathVolumeSource{Path: "/"},
1543 },
1544 },
1545 },
1546 Tolerations: []corev1.Toleration{
1547 {
1548 Operator: corev1.TolerationOpExists,
1549 },
1550 },
1551 },
1552 },
1553 },
1554 {
1555 name: "general profile",
1556 node: &corev1.Node{
1557 ObjectMeta: metav1.ObjectMeta{
1558 Name: "node-XXX",
1559 },
1560 },
1561 opts: &DebugOptions{
1562 Image: "busybox",
1563 PullPolicy: corev1.PullIfNotPresent,
1564 Profile: ProfileGeneral,
1565 },
1566 expected: &corev1.Pod{
1567 ObjectMeta: metav1.ObjectMeta{
1568 Name: "node-debugger-node-XXX-1",
1569 },
1570 Spec: corev1.PodSpec{
1571 Containers: []corev1.Container{
1572 {
1573 Name: "debugger",
1574 Image: "busybox",
1575 ImagePullPolicy: corev1.PullIfNotPresent,
1576 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1577 VolumeMounts: []corev1.VolumeMount{
1578 {
1579 MountPath: "/host",
1580 Name: "host-root",
1581 },
1582 },
1583 },
1584 },
1585 HostIPC: true,
1586 HostNetwork: true,
1587 HostPID: true,
1588 NodeName: "node-XXX",
1589 RestartPolicy: corev1.RestartPolicyNever,
1590 Volumes: []corev1.Volume{
1591 {
1592 Name: "host-root",
1593 VolumeSource: corev1.VolumeSource{
1594 HostPath: &corev1.HostPathVolumeSource{Path: "/"},
1595 },
1596 },
1597 },
1598 Tolerations: []corev1.Toleration{
1599 {
1600 Operator: corev1.TolerationOpExists,
1601 },
1602 },
1603 },
1604 },
1605 },
1606 {
1607 name: "baseline profile",
1608 node: &corev1.Node{
1609 ObjectMeta: metav1.ObjectMeta{
1610 Name: "node-XXX",
1611 },
1612 },
1613 opts: &DebugOptions{
1614 Image: "busybox",
1615 PullPolicy: corev1.PullIfNotPresent,
1616 Profile: ProfileBaseline,
1617 },
1618 expected: &corev1.Pod{
1619 ObjectMeta: metav1.ObjectMeta{
1620 Name: "node-debugger-node-XXX-1",
1621 },
1622 Spec: corev1.PodSpec{
1623 Containers: []corev1.Container{
1624 {
1625 Name: "debugger",
1626 Image: "busybox",
1627 ImagePullPolicy: corev1.PullIfNotPresent,
1628 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1629 VolumeMounts: nil,
1630 },
1631 },
1632 HostIPC: false,
1633 HostNetwork: false,
1634 HostPID: false,
1635 NodeName: "node-XXX",
1636 RestartPolicy: corev1.RestartPolicyNever,
1637 Volumes: nil,
1638 Tolerations: []corev1.Toleration{
1639 {
1640 Operator: corev1.TolerationOpExists,
1641 },
1642 },
1643 },
1644 },
1645 },
1646 {
1647 name: "restricted profile",
1648 node: &corev1.Node{
1649 ObjectMeta: metav1.ObjectMeta{
1650 Name: "node-XXX",
1651 },
1652 },
1653 opts: &DebugOptions{
1654 Image: "busybox",
1655 PullPolicy: corev1.PullIfNotPresent,
1656 Profile: ProfileRestricted,
1657 },
1658 expected: &corev1.Pod{
1659 ObjectMeta: metav1.ObjectMeta{
1660 Name: "node-debugger-node-XXX-1",
1661 },
1662 Spec: corev1.PodSpec{
1663 Containers: []corev1.Container{
1664 {
1665 Name: "debugger",
1666 Image: "busybox",
1667 ImagePullPolicy: corev1.PullIfNotPresent,
1668 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1669 VolumeMounts: nil,
1670 SecurityContext: &corev1.SecurityContext{
1671 RunAsNonRoot: pointer.Bool(true),
1672 Capabilities: &corev1.Capabilities{
1673 Drop: []corev1.Capability{"ALL"},
1674 },
1675 AllowPrivilegeEscalation: pointer.Bool(false),
1676 SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
1677 },
1678 },
1679 },
1680 HostIPC: false,
1681 HostNetwork: false,
1682 HostPID: false,
1683 NodeName: "node-XXX",
1684 RestartPolicy: corev1.RestartPolicyNever,
1685 Volumes: nil,
1686 Tolerations: []corev1.Toleration{
1687 {
1688 Operator: corev1.TolerationOpExists,
1689 },
1690 },
1691 },
1692 },
1693 },
1694 {
1695 name: "netadmin profile",
1696 node: &corev1.Node{
1697 ObjectMeta: metav1.ObjectMeta{
1698 Name: "node-XXX",
1699 },
1700 },
1701 opts: &DebugOptions{
1702 Image: "busybox",
1703 PullPolicy: corev1.PullIfNotPresent,
1704 Profile: ProfileNetadmin,
1705 },
1706 expected: &corev1.Pod{
1707 ObjectMeta: metav1.ObjectMeta{
1708 Name: "node-debugger-node-XXX-1",
1709 },
1710 Spec: corev1.PodSpec{
1711 Containers: []corev1.Container{
1712 {
1713 Name: "debugger",
1714 Image: "busybox",
1715 ImagePullPolicy: corev1.PullIfNotPresent,
1716 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1717 VolumeMounts: nil,
1718 SecurityContext: &corev1.SecurityContext{
1719 Capabilities: &corev1.Capabilities{
1720 Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
1721 },
1722 },
1723 },
1724 },
1725 HostIPC: true,
1726 HostNetwork: true,
1727 HostPID: true,
1728 NodeName: "node-XXX",
1729 RestartPolicy: corev1.RestartPolicyNever,
1730 Volumes: nil,
1731 Tolerations: []corev1.Toleration{
1732 {
1733 Operator: corev1.TolerationOpExists,
1734 },
1735 },
1736 },
1737 },
1738 },
1739 } {
1740 t.Run(tc.name, func(t *testing.T) {
1741 var err error
1742 tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
1743 if err != nil {
1744 t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
1745 }
1746 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
1747 suffixCounter = 0
1748
1749 pod, err := tc.opts.generateNodeDebugPod(tc.node)
1750 if err != nil {
1751 t.Fatalf("Fail to generate node debug pod: %v", err)
1752 }
1753 if diff := cmp.Diff(tc.expected, pod); diff != "" {
1754 t.Error("unexpected diff in generated object: (-want +got):\n", diff)
1755 }
1756 })
1757 }
1758 }
1759
1760 func TestGenerateNodeDebugPodCustomProfile(t *testing.T) {
1761 for _, tc := range []struct {
1762 name string
1763 node *corev1.Node
1764 opts *DebugOptions
1765 expected *corev1.Pod
1766 }{
1767 {
1768 name: "baseline profile",
1769 node: &corev1.Node{
1770 ObjectMeta: metav1.ObjectMeta{
1771 Name: "node-XXX",
1772 },
1773 },
1774 opts: &DebugOptions{
1775 Image: "busybox",
1776 PullPolicy: corev1.PullIfNotPresent,
1777 Profile: ProfileBaseline,
1778 CustomProfile: &corev1.Container{
1779 ImagePullPolicy: corev1.PullNever,
1780 Stdin: true,
1781 TTY: false,
1782 SecurityContext: &corev1.SecurityContext{
1783 Capabilities: &corev1.Capabilities{
1784 Drop: []corev1.Capability{"ALL"},
1785 },
1786 RunAsNonRoot: pointer.Bool(false),
1787 },
1788 },
1789 },
1790 expected: &corev1.Pod{
1791 ObjectMeta: metav1.ObjectMeta{
1792 Name: "node-debugger-node-XXX-1",
1793 },
1794 Spec: corev1.PodSpec{
1795 Containers: []corev1.Container{
1796 {
1797 Name: "debugger",
1798 Image: "busybox",
1799 ImagePullPolicy: corev1.PullNever,
1800 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1801 VolumeMounts: nil,
1802 Stdin: true,
1803 TTY: false,
1804 SecurityContext: &corev1.SecurityContext{
1805 RunAsNonRoot: pointer.Bool(false),
1806 Capabilities: &corev1.Capabilities{
1807 Drop: []corev1.Capability{"ALL"},
1808 },
1809 },
1810 },
1811 },
1812 HostIPC: false,
1813 HostNetwork: false,
1814 HostPID: false,
1815 NodeName: "node-XXX",
1816 RestartPolicy: corev1.RestartPolicyNever,
1817 Volumes: nil,
1818 Tolerations: []corev1.Toleration{
1819 {
1820 Operator: corev1.TolerationOpExists,
1821 },
1822 },
1823 },
1824 },
1825 },
1826 {
1827 name: "restricted profile",
1828 node: &corev1.Node{
1829 ObjectMeta: metav1.ObjectMeta{
1830 Name: "node-XXX",
1831 },
1832 },
1833 opts: &DebugOptions{
1834 Image: "busybox",
1835 PullPolicy: corev1.PullIfNotPresent,
1836 Profile: ProfileRestricted,
1837 CustomProfile: &corev1.Container{
1838 ImagePullPolicy: corev1.PullNever,
1839 Stdin: true,
1840 TTY: false,
1841 },
1842 },
1843 expected: &corev1.Pod{
1844 ObjectMeta: metav1.ObjectMeta{
1845 Name: "node-debugger-node-XXX-1",
1846 },
1847 Spec: corev1.PodSpec{
1848 Containers: []corev1.Container{
1849 {
1850 Name: "debugger",
1851 Image: "busybox",
1852 ImagePullPolicy: corev1.PullNever,
1853 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1854 VolumeMounts: nil,
1855 Stdin: true,
1856 TTY: false,
1857 SecurityContext: &corev1.SecurityContext{
1858 RunAsNonRoot: pointer.Bool(true),
1859 Capabilities: &corev1.Capabilities{
1860 Drop: []corev1.Capability{"ALL"},
1861 },
1862 AllowPrivilegeEscalation: pointer.Bool(false),
1863 SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
1864 },
1865 },
1866 },
1867 HostIPC: false,
1868 HostNetwork: false,
1869 HostPID: false,
1870 NodeName: "node-XXX",
1871 RestartPolicy: corev1.RestartPolicyNever,
1872 Volumes: nil,
1873 Tolerations: []corev1.Toleration{
1874 {
1875 Operator: corev1.TolerationOpExists,
1876 },
1877 },
1878 },
1879 },
1880 },
1881 {
1882 name: "netadmin profile",
1883 node: &corev1.Node{
1884 ObjectMeta: metav1.ObjectMeta{
1885 Name: "node-XXX",
1886 },
1887 },
1888 opts: &DebugOptions{
1889 Image: "busybox",
1890 PullPolicy: corev1.PullIfNotPresent,
1891 Profile: ProfileNetadmin,
1892 CustomProfile: &corev1.Container{
1893 Env: []corev1.EnvVar{
1894 {
1895 Name: "TEST_KEY",
1896 Value: "TEST_VALUE",
1897 },
1898 },
1899 },
1900 },
1901 expected: &corev1.Pod{
1902 Spec: corev1.PodSpec{
1903 Containers: []corev1.Container{
1904 {
1905 Name: "debugger",
1906 Image: "busybox",
1907 ImagePullPolicy: corev1.PullIfNotPresent,
1908 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1909 Env: []corev1.EnvVar{
1910 {
1911 Name: "TEST_KEY",
1912 Value: "TEST_VALUE",
1913 },
1914 },
1915 VolumeMounts: nil,
1916 SecurityContext: &corev1.SecurityContext{
1917 Capabilities: &corev1.Capabilities{
1918 Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
1919 },
1920 },
1921 },
1922 },
1923 HostIPC: true,
1924 HostNetwork: true,
1925 HostPID: true,
1926 NodeName: "node-XXX",
1927 RestartPolicy: corev1.RestartPolicyNever,
1928 Volumes: nil,
1929 Tolerations: []corev1.Toleration{
1930 {
1931 Operator: corev1.TolerationOpExists,
1932 },
1933 },
1934 },
1935 },
1936 },
1937 {
1938 name: "sysadmin profile",
1939 node: &corev1.Node{
1940 ObjectMeta: metav1.ObjectMeta{
1941 Name: "node-XXX",
1942 },
1943 },
1944 opts: &DebugOptions{
1945 Image: "busybox",
1946 PullPolicy: corev1.PullIfNotPresent,
1947 Profile: ProfileSysadmin,
1948 CustomProfile: &corev1.Container{
1949 Env: []corev1.EnvVar{
1950 {
1951 Name: "TEST_KEY",
1952 Value: "TEST_VALUE",
1953 },
1954 },
1955 VolumeMounts: []corev1.VolumeMount{
1956 {
1957 Name: "host-root",
1958 ReadOnly: true,
1959 MountPath: "/host",
1960 },
1961 },
1962 },
1963 },
1964 expected: &corev1.Pod{
1965 Spec: corev1.PodSpec{
1966 Containers: []corev1.Container{
1967 {
1968 Name: "debugger",
1969 Image: "busybox",
1970 ImagePullPolicy: corev1.PullIfNotPresent,
1971 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
1972 Env: []corev1.EnvVar{
1973 {
1974 Name: "TEST_KEY",
1975 Value: "TEST_VALUE",
1976 },
1977 },
1978 VolumeMounts: []corev1.VolumeMount{
1979 {
1980 Name: "host-root",
1981 ReadOnly: true,
1982 MountPath: "/host",
1983 },
1984 },
1985 SecurityContext: &corev1.SecurityContext{
1986 Privileged: pointer.Bool(true),
1987 },
1988 },
1989 },
1990 HostIPC: true,
1991 HostNetwork: true,
1992 HostPID: true,
1993 NodeName: "node-XXX",
1994 RestartPolicy: corev1.RestartPolicyNever,
1995 Volumes: []corev1.Volume{
1996 {
1997 Name: "host-root",
1998 VolumeSource: corev1.VolumeSource{
1999 HostPath: &corev1.HostPathVolumeSource{
2000 Path: "/",
2001 },
2002 },
2003 },
2004 },
2005 Tolerations: []corev1.Toleration{
2006 {
2007 Operator: corev1.TolerationOpExists,
2008 },
2009 },
2010 },
2011 },
2012 },
2013 } {
2014
2015 t.Run(tc.name, func(t *testing.T) {
2016 cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
2017 var err error
2018 tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
2019 if err != nil {
2020 t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
2021 }
2022 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
2023
2024 pod, err := tc.opts.generateNodeDebugPod(tc.node)
2025 if err != nil {
2026 t.Fatalf("Fail to generate node debug pod: %v", err)
2027 }
2028 tc.expected.Name = pod.Name
2029 if diff := cmp.Diff(tc.expected, pod); diff != "" {
2030 t.Error("unexpected diff in generated object: (-want +got):\n", diff)
2031 }
2032 })
2033 })
2034 }
2035 }
2036
2037 func TestGenerateCopyDebugPodCustomProfile(t *testing.T) {
2038 for _, tc := range []struct {
2039 name string
2040 copyPod *corev1.Pod
2041 opts *DebugOptions
2042 expected *corev1.Pod
2043 }{
2044 {
2045 name: "baseline profile",
2046 copyPod: &corev1.Pod{
2047 Spec: corev1.PodSpec{
2048 ServiceAccountName: "test",
2049 NodeName: "test-node",
2050 },
2051 },
2052 opts: &DebugOptions{
2053 SameNode: true,
2054 Image: "busybox",
2055 PullPolicy: corev1.PullIfNotPresent,
2056 Profile: ProfileBaseline,
2057 CustomProfile: &corev1.Container{
2058 ImagePullPolicy: corev1.PullNever,
2059 Stdin: true,
2060 TTY: false,
2061 SecurityContext: &corev1.SecurityContext{
2062 Capabilities: &corev1.Capabilities{
2063 Drop: []corev1.Capability{"ALL"},
2064 },
2065 RunAsNonRoot: pointer.Bool(false),
2066 },
2067 },
2068 },
2069 expected: &corev1.Pod{
2070 Spec: corev1.PodSpec{
2071 ServiceAccountName: "test",
2072 NodeName: "test-node",
2073 Containers: []corev1.Container{
2074 {
2075 Image: "busybox",
2076 ImagePullPolicy: corev1.PullNever,
2077 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2078 VolumeMounts: nil,
2079 Stdin: true,
2080 TTY: false,
2081 SecurityContext: &corev1.SecurityContext{
2082 RunAsNonRoot: pointer.Bool(false),
2083 Capabilities: &corev1.Capabilities{
2084 Drop: []corev1.Capability{"ALL"},
2085 },
2086 },
2087 },
2088 },
2089 HostIPC: false,
2090 HostNetwork: false,
2091 HostPID: false,
2092 Volumes: nil,
2093 ShareProcessNamespace: pointer.Bool(true),
2094 },
2095 },
2096 },
2097 {
2098 name: "restricted profile",
2099 copyPod: &corev1.Pod{
2100 Spec: corev1.PodSpec{
2101 ServiceAccountName: "test",
2102 NodeName: "test-node",
2103 },
2104 },
2105 opts: &DebugOptions{
2106 SameNode: true,
2107 Image: "busybox",
2108 PullPolicy: corev1.PullIfNotPresent,
2109 Profile: ProfileRestricted,
2110 CustomProfile: &corev1.Container{
2111 ImagePullPolicy: corev1.PullNever,
2112 Stdin: true,
2113 TTY: false,
2114 SecurityContext: &corev1.SecurityContext{
2115 Capabilities: &corev1.Capabilities{
2116 Drop: []corev1.Capability{"ALL"},
2117 },
2118 RunAsNonRoot: pointer.Bool(false),
2119 },
2120 },
2121 },
2122 expected: &corev1.Pod{
2123 Spec: corev1.PodSpec{
2124 ServiceAccountName: "test",
2125 NodeName: "test-node",
2126 Containers: []corev1.Container{
2127 {
2128 Image: "busybox",
2129 ImagePullPolicy: corev1.PullNever,
2130 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2131 VolumeMounts: nil,
2132 Stdin: true,
2133 TTY: false,
2134 SecurityContext: &corev1.SecurityContext{
2135 AllowPrivilegeEscalation: pointer.Bool(false),
2136 RunAsNonRoot: pointer.Bool(false),
2137 Capabilities: &corev1.Capabilities{
2138 Drop: []corev1.Capability{"ALL"},
2139 },
2140 SeccompProfile: &corev1.SeccompProfile{
2141 Type: corev1.SeccompProfileTypeRuntimeDefault,
2142 LocalhostProfile: nil,
2143 },
2144 },
2145 },
2146 },
2147 HostIPC: false,
2148 HostNetwork: false,
2149 HostPID: false,
2150 Volumes: nil,
2151 ShareProcessNamespace: pointer.Bool(true),
2152 },
2153 },
2154 },
2155 {
2156 name: "sysadmin profile",
2157 copyPod: &corev1.Pod{
2158 Spec: corev1.PodSpec{
2159 ServiceAccountName: "test",
2160 NodeName: "test-node",
2161 },
2162 },
2163 opts: &DebugOptions{
2164 SameNode: true,
2165 Image: "busybox",
2166 PullPolicy: corev1.PullIfNotPresent,
2167 Profile: ProfileRestricted,
2168 CustomProfile: &corev1.Container{
2169 ImagePullPolicy: corev1.PullNever,
2170 Stdin: true,
2171 TTY: false,
2172 SecurityContext: &corev1.SecurityContext{
2173 Capabilities: &corev1.Capabilities{
2174 Drop: []corev1.Capability{"ALL"},
2175 },
2176 RunAsNonRoot: pointer.Bool(false),
2177 },
2178 },
2179 },
2180 expected: &corev1.Pod{
2181 Spec: corev1.PodSpec{
2182 ServiceAccountName: "test",
2183 NodeName: "test-node",
2184 Containers: []corev1.Container{
2185 {
2186 Image: "busybox",
2187 ImagePullPolicy: corev1.PullNever,
2188 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2189 VolumeMounts: nil,
2190 Stdin: true,
2191 TTY: false,
2192 SecurityContext: &corev1.SecurityContext{
2193 AllowPrivilegeEscalation: pointer.Bool(false),
2194 RunAsNonRoot: pointer.Bool(false),
2195 Capabilities: &corev1.Capabilities{
2196 Drop: []corev1.Capability{"ALL"},
2197 },
2198 SeccompProfile: &corev1.SeccompProfile{
2199 Type: corev1.SeccompProfileTypeRuntimeDefault,
2200 LocalhostProfile: nil,
2201 },
2202 },
2203 },
2204 },
2205 HostIPC: false,
2206 HostNetwork: false,
2207 HostPID: false,
2208 Volumes: nil,
2209 ShareProcessNamespace: pointer.Bool(true),
2210 },
2211 },
2212 },
2213 } {
2214
2215 t.Run(tc.name, func(t *testing.T) {
2216 cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
2217 var err error
2218 tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
2219 if err != nil {
2220 t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
2221 }
2222 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
2223
2224 pod, dc, err := tc.opts.generatePodCopyWithDebugContainer(tc.copyPod)
2225 if err != nil {
2226 t.Fatalf("Fail to generate node debug pod: %v", err)
2227 }
2228 tc.expected.Spec.Containers[0].Name = dc
2229 if diff := cmp.Diff(tc.expected, pod); diff != "" {
2230 t.Error("unexpected diff in generated object: (-want +got):\n", diff)
2231 }
2232 })
2233 })
2234 }
2235 }
2236
2237 func TestGenerateEphemeralDebugPodCustomProfile(t *testing.T) {
2238 for _, tc := range []struct {
2239 name string
2240 copyPod *corev1.Pod
2241 opts *DebugOptions
2242 expected *corev1.Pod
2243 }{
2244 {
2245 name: "baseline profile",
2246 copyPod: &corev1.Pod{
2247 Spec: corev1.PodSpec{
2248 ServiceAccountName: "test",
2249 NodeName: "test-node",
2250 },
2251 },
2252 opts: &DebugOptions{
2253 SameNode: true,
2254 Image: "busybox",
2255 PullPolicy: corev1.PullIfNotPresent,
2256 Profile: ProfileBaseline,
2257 CustomProfile: &corev1.Container{
2258 ImagePullPolicy: corev1.PullNever,
2259 Stdin: true,
2260 TTY: false,
2261 SecurityContext: &corev1.SecurityContext{
2262 Capabilities: &corev1.Capabilities{
2263 Drop: []corev1.Capability{"ALL"},
2264 },
2265 RunAsNonRoot: pointer.Bool(false),
2266 },
2267 },
2268 },
2269 expected: &corev1.Pod{
2270 Spec: corev1.PodSpec{
2271 ServiceAccountName: "test",
2272 NodeName: "test-node",
2273 EphemeralContainers: []corev1.EphemeralContainer{
2274 {
2275 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
2276 Name: "debugger-1",
2277 Image: "busybox",
2278 ImagePullPolicy: corev1.PullNever,
2279 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2280 VolumeMounts: nil,
2281 Stdin: true,
2282 TTY: false,
2283 SecurityContext: &corev1.SecurityContext{
2284 RunAsNonRoot: pointer.Bool(false),
2285 Capabilities: &corev1.Capabilities{
2286 Drop: []corev1.Capability{"ALL"},
2287 },
2288 },
2289 },
2290 },
2291 },
2292 HostIPC: false,
2293 HostNetwork: false,
2294 HostPID: false,
2295 Volumes: nil,
2296 },
2297 },
2298 },
2299 {
2300 name: "restricted profile",
2301 copyPod: &corev1.Pod{
2302 Spec: corev1.PodSpec{
2303 ServiceAccountName: "test",
2304 NodeName: "test-node",
2305 },
2306 },
2307 opts: &DebugOptions{
2308 SameNode: true,
2309 Image: "busybox",
2310 PullPolicy: corev1.PullIfNotPresent,
2311 Profile: ProfileRestricted,
2312 CustomProfile: &corev1.Container{
2313 ImagePullPolicy: corev1.PullNever,
2314 Stdin: true,
2315 TTY: false,
2316 SecurityContext: &corev1.SecurityContext{
2317 Capabilities: &corev1.Capabilities{
2318 Drop: []corev1.Capability{"ALL"},
2319 },
2320 RunAsNonRoot: pointer.Bool(false),
2321 },
2322 },
2323 },
2324 expected: &corev1.Pod{
2325 Spec: corev1.PodSpec{
2326 ServiceAccountName: "test",
2327 NodeName: "test-node",
2328 EphemeralContainers: []corev1.EphemeralContainer{
2329 {
2330 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
2331 Name: "debugger-1",
2332 Image: "busybox",
2333 ImagePullPolicy: corev1.PullNever,
2334 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2335 VolumeMounts: nil,
2336 Stdin: true,
2337 TTY: false,
2338 SecurityContext: &corev1.SecurityContext{
2339 AllowPrivilegeEscalation: pointer.Bool(false),
2340 RunAsNonRoot: pointer.Bool(false),
2341 Capabilities: &corev1.Capabilities{
2342 Drop: []corev1.Capability{"ALL"},
2343 },
2344 SeccompProfile: &corev1.SeccompProfile{
2345 Type: corev1.SeccompProfileTypeRuntimeDefault,
2346 LocalhostProfile: nil,
2347 },
2348 },
2349 },
2350 },
2351 },
2352 HostIPC: false,
2353 HostNetwork: false,
2354 HostPID: false,
2355 Volumes: nil,
2356 },
2357 },
2358 },
2359 {
2360 name: "sysadmin profile",
2361 copyPod: &corev1.Pod{
2362 Spec: corev1.PodSpec{
2363 ServiceAccountName: "test",
2364 NodeName: "test-node",
2365 },
2366 },
2367 opts: &DebugOptions{
2368 SameNode: true,
2369 Image: "busybox",
2370 PullPolicy: corev1.PullIfNotPresent,
2371 Profile: ProfileRestricted,
2372 CustomProfile: &corev1.Container{
2373 ImagePullPolicy: corev1.PullNever,
2374 Stdin: true,
2375 TTY: false,
2376 SecurityContext: &corev1.SecurityContext{
2377 Capabilities: &corev1.Capabilities{
2378 Drop: []corev1.Capability{"ALL"},
2379 },
2380 RunAsNonRoot: pointer.Bool(false),
2381 },
2382 },
2383 },
2384 expected: &corev1.Pod{
2385 Spec: corev1.PodSpec{
2386 ServiceAccountName: "test",
2387 NodeName: "test-node",
2388 EphemeralContainers: []corev1.EphemeralContainer{
2389 {
2390 EphemeralContainerCommon: corev1.EphemeralContainerCommon{
2391 Name: "debugger-1",
2392 Image: "busybox",
2393 ImagePullPolicy: corev1.PullNever,
2394 TerminationMessagePolicy: corev1.TerminationMessageReadFile,
2395 VolumeMounts: nil,
2396 Stdin: true,
2397 TTY: false,
2398 SecurityContext: &corev1.SecurityContext{
2399 AllowPrivilegeEscalation: pointer.Bool(false),
2400 RunAsNonRoot: pointer.Bool(false),
2401 Capabilities: &corev1.Capabilities{
2402 Drop: []corev1.Capability{"ALL"},
2403 },
2404 SeccompProfile: &corev1.SeccompProfile{
2405 Type: corev1.SeccompProfileTypeRuntimeDefault,
2406 LocalhostProfile: nil,
2407 },
2408 },
2409 },
2410 },
2411 },
2412 HostIPC: false,
2413 HostNetwork: false,
2414 HostPID: false,
2415 Volumes: nil,
2416 },
2417 },
2418 },
2419 } {
2420
2421 t.Run(tc.name, func(t *testing.T) {
2422 cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
2423 var err error
2424 tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
2425 if err != nil {
2426 t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
2427 }
2428 tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
2429
2430 pod, ec, err := tc.opts.generateDebugContainer(tc.copyPod)
2431 if err != nil {
2432 t.Fatalf("Fail to generate node debug pod: %v", err)
2433 }
2434 tc.expected.Spec.EphemeralContainers[0].Name = ec.Name
2435 if diff := cmp.Diff(tc.expected, pod); diff != "" {
2436 t.Error("unexpected diff in generated object: (-want +got):\n", diff)
2437 }
2438 })
2439 })
2440 }
2441 }
2442
2443 func TestCompleteAndValidate(t *testing.T) {
2444 tf := cmdtesting.NewTestFactory().WithNamespace("test")
2445 ioStreams, _, _, _ := genericiooptions.NewTestIOStreams()
2446 cmpFilter := cmp.FilterPath(func(p cmp.Path) bool {
2447 switch p.String() {
2448
2449 case "IOStreams", "Applier":
2450 return true
2451 }
2452 return false
2453 }, cmp.Ignore())
2454
2455 tests := []struct {
2456 name, args string
2457 wantOpts *DebugOptions
2458 wantError bool
2459 }{
2460 {
2461 name: "No targets",
2462 args: "--image=image",
2463 wantError: true,
2464 },
2465 {
2466 name: "Invalid environment variables",
2467 args: "--image=busybox --env=FOO mypod",
2468 wantError: true,
2469 },
2470 {
2471 name: "Invalid image name",
2472 args: "--image=image:label@deadbeef mypod",
2473 wantError: true,
2474 },
2475 {
2476 name: "Invalid pull policy",
2477 args: "--image=image --image-pull-policy=whenever-you-feel-like-it",
2478 wantError: true,
2479 },
2480 {
2481 name: "TTY without stdin",
2482 args: "--image=image --tty",
2483 wantError: true,
2484 },
2485 {
2486 name: "Set image pull policy",
2487 args: "--image=busybox --image-pull-policy=Always mypod",
2488 wantOpts: &DebugOptions{
2489 Args: []string{},
2490 Image: "busybox",
2491 Namespace: "test",
2492 PullPolicy: corev1.PullPolicy("Always"),
2493 ShareProcesses: true,
2494 Profile: ProfileLegacy,
2495 TargetNames: []string{"mypod"},
2496 },
2497 },
2498 {
2499 name: "Multiple targets",
2500 args: "--image=busybox mypod1 mypod2",
2501 wantOpts: &DebugOptions{
2502 Args: []string{},
2503 Image: "busybox",
2504 Namespace: "test",
2505 ShareProcesses: true,
2506 Profile: ProfileLegacy,
2507 TargetNames: []string{"mypod1", "mypod2"},
2508 },
2509 },
2510 {
2511 name: "Arguments with dash",
2512 args: "--image=busybox mypod1 mypod2 -- echo 1 2",
2513 wantOpts: &DebugOptions{
2514 Args: []string{"echo", "1", "2"},
2515 Image: "busybox",
2516 Namespace: "test",
2517 ShareProcesses: true,
2518 Profile: ProfileLegacy,
2519 TargetNames: []string{"mypod1", "mypod2"},
2520 },
2521 },
2522 {
2523 name: "Interactive no attach",
2524 args: "-ti --image=busybox --attach=false mypod",
2525 wantOpts: &DebugOptions{
2526 Args: []string{},
2527 Attach: false,
2528 Image: "busybox",
2529 Interactive: true,
2530 Namespace: "test",
2531 ShareProcesses: true,
2532 Profile: ProfileLegacy,
2533 TargetNames: []string{"mypod"},
2534 TTY: true,
2535 },
2536 },
2537 {
2538 name: "Set environment variables",
2539 args: "--image=busybox --env=FOO=BAR mypod",
2540 wantOpts: &DebugOptions{
2541 Args: []string{},
2542 Env: []corev1.EnvVar{{Name: "FOO", Value: "BAR"}},
2543 Image: "busybox",
2544 Namespace: "test",
2545 ShareProcesses: true,
2546 Profile: ProfileLegacy,
2547 TargetNames: []string{"mypod"},
2548 },
2549 },
2550 {
2551 name: "Ephemeral container: interactive session minimal args",
2552 args: "mypod -it --image=busybox",
2553 wantOpts: &DebugOptions{
2554 Args: []string{},
2555 Attach: true,
2556 Image: "busybox",
2557 Interactive: true,
2558 Namespace: "test",
2559 ShareProcesses: true,
2560 Profile: ProfileLegacy,
2561 TargetNames: []string{"mypod"},
2562 TTY: true,
2563 },
2564 },
2565 {
2566 name: "Ephemeral container: non-interactive debugger with image and name",
2567 args: "--image=myproj/debug-tools --image-pull-policy=Always -c debugger mypod",
2568 wantOpts: &DebugOptions{
2569 Args: []string{},
2570 Container: "debugger",
2571 Image: "myproj/debug-tools",
2572 Namespace: "test",
2573 PullPolicy: corev1.PullPolicy("Always"),
2574 Profile: ProfileLegacy,
2575 ShareProcesses: true,
2576 TargetNames: []string{"mypod"},
2577 },
2578 },
2579 {
2580 name: "Ephemeral container: no image specified",
2581 args: "mypod",
2582 wantError: true,
2583 },
2584 {
2585 name: "Ephemeral container: no image but args",
2586 args: "mypod -- echo 1 2",
2587 wantError: true,
2588 },
2589 {
2590 name: "Ephemeral container: replace not allowed",
2591 args: "--replace --image=busybox mypod",
2592 wantError: true,
2593 },
2594 {
2595 name: "Ephemeral container: same-node not allowed",
2596 args: "--same-node --image=busybox mypod",
2597 wantError: true,
2598 },
2599 {
2600 name: "Ephemeral container: incompatible with --set-image",
2601 args: "--set-image=*=busybox mypod",
2602 wantError: true,
2603 },
2604 {
2605 name: "Pod copy: interactive debug container minimal args",
2606 args: "mypod -it --image=busybox --copy-to=my-debugger",
2607 wantOpts: &DebugOptions{
2608 Args: []string{},
2609 Attach: true,
2610 CopyTo: "my-debugger",
2611 Image: "busybox",
2612 Interactive: true,
2613 Namespace: "test",
2614 ShareProcesses: true,
2615 Profile: ProfileLegacy,
2616 TargetNames: []string{"mypod"},
2617 TTY: true,
2618 },
2619 },
2620 {
2621 name: "Pod copy: non-interactive with debug container, image name and command",
2622 args: "mypod --image=busybox --container=my-container --copy-to=my-debugger -- sleep 1d",
2623 wantOpts: &DebugOptions{
2624 Args: []string{"sleep", "1d"},
2625 Container: "my-container",
2626 CopyTo: "my-debugger",
2627 Image: "busybox",
2628 Namespace: "test",
2629 ShareProcesses: true,
2630 Profile: ProfileLegacy,
2631 TargetNames: []string{"mypod"},
2632 },
2633 },
2634 {
2635 name: "Pod copy: explicit attach",
2636 args: "mypod --image=busybox --copy-to=my-debugger --attach -- sleep 1d",
2637 wantOpts: &DebugOptions{
2638 Args: []string{"sleep", "1d"},
2639 Attach: true,
2640 CopyTo: "my-debugger",
2641 Image: "busybox",
2642 Namespace: "test",
2643 ShareProcesses: true,
2644 Profile: ProfileLegacy,
2645 TargetNames: []string{"mypod"},
2646 },
2647 },
2648 {
2649 name: "Pod copy: replace single image of existing container",
2650 args: "mypod --image=busybox --container=my-container --copy-to=my-debugger",
2651 wantOpts: &DebugOptions{
2652 Args: []string{},
2653 Container: "my-container",
2654 CopyTo: "my-debugger",
2655 Image: "busybox",
2656 Namespace: "test",
2657 ShareProcesses: true,
2658 Profile: ProfileLegacy,
2659 TargetNames: []string{"mypod"},
2660 },
2661 },
2662 {
2663 name: "Pod copy: mutate existing container images",
2664 args: "mypod --set-image=*=busybox,app=app-debugger --copy-to=my-debugger",
2665 wantOpts: &DebugOptions{
2666 Args: []string{},
2667 CopyTo: "my-debugger",
2668 Namespace: "test",
2669 SetImages: map[string]string{
2670 "*": "busybox",
2671 "app": "app-debugger",
2672 },
2673 ShareProcesses: true,
2674 Profile: ProfileLegacy,
2675 TargetNames: []string{"mypod"},
2676 },
2677 },
2678 {
2679 name: "Pod copy: add container and also mutate images",
2680 args: "mypod -it --copy-to=my-debugger --image=debian --set-image=app=app:debug,sidecar=sidecar:debug",
2681 wantOpts: &DebugOptions{
2682 Args: []string{},
2683 Attach: true,
2684 CopyTo: "my-debugger",
2685 Image: "debian",
2686 Interactive: true,
2687 Namespace: "test",
2688 SetImages: map[string]string{
2689 "app": "app:debug",
2690 "sidecar": "sidecar:debug",
2691 },
2692 ShareProcesses: true,
2693 Profile: ProfileLegacy,
2694 TargetNames: []string{"mypod"},
2695 TTY: true,
2696 },
2697 },
2698 {
2699 name: "Pod copy: change command",
2700 args: "mypod -it --copy-to=my-debugger --container=mycontainer -- sh",
2701 wantOpts: &DebugOptions{
2702 Attach: true,
2703 Args: []string{"sh"},
2704 Container: "mycontainer",
2705 CopyTo: "my-debugger",
2706 Interactive: true,
2707 Namespace: "test",
2708 ShareProcesses: true,
2709 Profile: ProfileLegacy,
2710 TargetNames: []string{"mypod"},
2711 TTY: true,
2712 },
2713 },
2714 {
2715 name: "Pod copy: no image specified",
2716 args: "mypod -it --copy-to=my-debugger",
2717 wantError: true,
2718 },
2719 {
2720 name: "Pod copy: args but no image specified",
2721 args: "mypod --copy-to=my-debugger -- echo milo",
2722 wantError: true,
2723 },
2724 {
2725 name: "Pod copy: --target not allowed",
2726 args: "mypod --target --image=busybox --copy-to=my-debugger",
2727 wantError: true,
2728 },
2729 {
2730 name: "Pod copy: invalid --set-image",
2731 args: "mypod --set-image=*=SUPERGOODIMAGE#1!!!! --copy-to=my-debugger",
2732 wantError: true,
2733 },
2734 {
2735 name: "Pod copy: specifying attach without existing or newly created container",
2736 args: "mypod --set-image=*=busybox --copy-to=my-debugger --attach",
2737 wantError: true,
2738 },
2739 {
2740 name: "Node: interactive session minimal args",
2741 args: "node/mynode -it --image=busybox",
2742 wantOpts: &DebugOptions{
2743 Args: []string{},
2744 Attach: true,
2745 Image: "busybox",
2746 Interactive: true,
2747 Namespace: "test",
2748 ShareProcesses: true,
2749 Profile: ProfileLegacy,
2750 TargetNames: []string{"node/mynode"},
2751 TTY: true,
2752 },
2753 },
2754 {
2755 name: "Node: no image specified",
2756 args: "node/mynode -it",
2757 wantError: true,
2758 },
2759 {
2760 name: "Node: --replace not allowed",
2761 args: "--image=busybox --replace node/mynode",
2762 wantError: true,
2763 },
2764 {
2765 name: "Node: --same-node not allowed",
2766 args: "--image=busybox --same-node node/mynode",
2767 wantError: true,
2768 },
2769 {
2770 name: "Node: --set-image not allowed",
2771 args: "--image=busybox --set-image=*=busybox node/mynode",
2772 wantError: true,
2773 },
2774 {
2775 name: "Node: --target not allowed",
2776 args: "node/mynode --target --image=busybox",
2777 wantError: true,
2778 },
2779 }
2780
2781 for _, tc := range tests {
2782 t.Run(tc.name, func(t *testing.T) {
2783 opts := NewDebugOptions(ioStreams)
2784 var gotError error
2785 cmd := &cobra.Command{
2786 Run: func(cmd *cobra.Command, args []string) {
2787 gotError = opts.Complete(tf, cmd, args)
2788 if gotError != nil {
2789 return
2790 }
2791 gotError = opts.Validate()
2792 },
2793 }
2794 cmd.SetArgs(strings.Split(tc.args, " "))
2795 opts.AddFlags(cmd)
2796
2797 cmdError := cmd.Execute()
2798
2799 if tc.wantError {
2800 if cmdError != nil || gotError != nil {
2801 return
2802 }
2803 t.Fatalf("CompleteAndValidate got nil errors but wantError: %v", tc.wantError)
2804 } else if cmdError != nil {
2805 t.Fatalf("cmd.Execute got error '%v' executing test cobra.Command, wantError: %v", cmdError, tc.wantError)
2806 } else if gotError != nil {
2807 t.Fatalf("CompleteAndValidate got error: '%v', wantError: %v", gotError, tc.wantError)
2808 }
2809
2810 if diff := cmp.Diff(tc.wantOpts, opts, cmpFilter, cmpopts.IgnoreFields(DebugOptions{},
2811 "attachChanged", "shareProcessedChanged", "podClient", "WarningPrinter", "Applier", "explicitNamespace", "Builder", "AttachFunc")); diff != "" {
2812 t.Error("CompleteAndValidate unexpected diff in generated object: (-want +got):\n", diff)
2813 }
2814 })
2815 }
2816 }
2817
View as plain text