diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go index 80d32166f9f..e46280b259c 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/debug/debug_test.go @@ -22,16 +22,15 @@ import ( "testing" "time" - "github.com/spf13/cobra" - "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "k8s.io/utils/pointer" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cli-runtime/pkg/genericclioptions" cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + "k8s.io/utils/pointer" ) func TestGenerateDebugContainer(t *testing.T) { @@ -361,6 +360,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Container: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -398,6 +398,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Image: "busybox", PullPolicy: corev1.PullIfNotPresent, SameNode: true, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -435,6 +436,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Container: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -481,6 +483,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Container: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -524,6 +527,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Name: "TEST", Value: "test", }}, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -568,6 +572,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Args: []string{"/bin/echo", "one", "two", "three"}, Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -610,6 +615,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { ArgsOnly: true, Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -650,6 +656,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Container: "debugger", Args: []string{"sleep", "1d"}, PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -690,6 +697,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { CopyTo: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -728,6 +736,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { CopyTo: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -766,6 +775,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { CopyTo: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -820,6 +830,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { CopyTo: "debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -873,6 +884,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { PullPolicy: corev1.PullIfNotPresent, ShareProcesses: true, shareProcessedChanged: true, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -914,6 +926,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Container: "app", Image: "busybox", TargetNames: []string{"myapp"}, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "myapp"}, @@ -940,6 +953,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { CopyTo: "myapp-copy", Container: "app", SetImages: map[string]string{"app": "busybox"}, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -969,6 +983,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { opts: &DebugOptions{ CopyTo: "myapp-copy", SetImages: map[string]string{"*": "busybox"}, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -998,6 +1013,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { opts: &DebugOptions{ CopyTo: "myapp-copy", SetImages: map[string]string{"*": "busybox", "app": "app-debugger"}, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -1032,6 +1048,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { Interactive: true, TargetNames: []string{"mypod"}, TTY: true, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "mypod"}, @@ -1075,6 +1092,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { ShareProcesses: true, TargetNames: []string{"mypod"}, TTY: true, + Profile: ProfileLegacy, }, havePod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "mypod"}, @@ -1102,12 +1120,179 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { }, }, }, + { + name: "general profile", + opts: &DebugOptions{ + CopyTo: "debugger", + Container: "debugger", + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileGeneral, + }, + havePod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "target", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + }, + }, + NodeName: "node-1", + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debugger", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"SYS_PTRACE"}, + }, + }, + }, + }, + ShareProcessNamespace: pointer.Bool(true), + }, + }, + }, + { + name: "baseline profile", + opts: &DebugOptions{ + CopyTo: "debugger", + Container: "debugger", + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileBaseline, + }, + havePod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "target", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + }, + }, + NodeName: "node-1", + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debugger", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + ShareProcessNamespace: pointer.Bool(true), + }, + }, + }, + { + name: "restricted profile", + opts: &DebugOptions{ + CopyTo: "debugger", + Container: "debugger", + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileRestricted, + }, + havePod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "target", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + }, + }, + NodeName: "node-1", + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debugger", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Drop: []corev1.Capability{"ALL"}, + }, + RunAsNonRoot: pointer.Bool(true), + }, + }, + }, + ShareProcessNamespace: pointer.Bool(true), + }, + }, + }, + { + name: "netadmin profile", + opts: &DebugOptions{ + CopyTo: "debugger", + Container: "debugger", + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileNetadmin, + }, + havePod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "target", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + }, + }, + NodeName: "node-1", + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "debugger", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"NET_ADMIN"}, + }, + }, + }, + }, + }, + }, + }, } { t.Run(tc.name, func(t *testing.T) { var err error - tc.opts.Applier, err = NewProfileApplier(ProfileLegacy) + tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile) if err != nil { - t.Fatalf("Fail to create legacy profile: %v", err) + t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err) } tc.opts.IOStreams = genericclioptions.NewTestIOStreamsDiscard() suffixCounter = 0 @@ -1147,6 +1332,7 @@ func TestGenerateNodeDebugPod(t *testing.T) { opts: &DebugOptions{ Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -1200,6 +1386,7 @@ func TestGenerateNodeDebugPod(t *testing.T) { Container: "custom-debugger", Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -1255,6 +1442,7 @@ func TestGenerateNodeDebugPod(t *testing.T) { Args: []string{"echo", "one", "two", "three"}, Image: "busybox", PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileLegacy, }, expected: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -1297,12 +1485,190 @@ func TestGenerateNodeDebugPod(t *testing.T) { }, }, }, + { + name: "general profile", + node: &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-XXX", + }, + }, + opts: &DebugOptions{ + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileGeneral, + }, + expected: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-debugger-node-XXX-1", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/host", + Name: "host-root", + }, + }, + }, + }, + HostIPC: true, + HostNetwork: true, + HostPID: true, + NodeName: "node-XXX", + RestartPolicy: corev1.RestartPolicyNever, + Volumes: []corev1.Volume{ + { + Name: "host-root", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{Path: "/"}, + }, + }, + }, + Tolerations: []corev1.Toleration{ + { + Operator: corev1.TolerationOpExists, + }, + }, + }, + }, + }, + { + name: "baseline profile", + node: &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-XXX", + }, + }, + opts: &DebugOptions{ + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileBaseline, + }, + expected: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-debugger-node-XXX-1", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeMounts: nil, + }, + }, + HostIPC: false, + HostNetwork: false, + HostPID: false, + NodeName: "node-XXX", + RestartPolicy: corev1.RestartPolicyNever, + Volumes: nil, + Tolerations: []corev1.Toleration{ + { + Operator: corev1.TolerationOpExists, + }, + }, + }, + }, + }, + { + name: "restricted profile", + node: &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-XXX", + }, + }, + opts: &DebugOptions{ + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileRestricted, + }, + expected: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-debugger-node-XXX-1", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeMounts: nil, + }, + }, + HostIPC: false, + HostNetwork: false, + HostPID: false, + NodeName: "node-XXX", + RestartPolicy: corev1.RestartPolicyNever, + Volumes: nil, + Tolerations: []corev1.Toleration{ + { + Operator: corev1.TolerationOpExists, + }, + }, + }, + }, + }, + { + name: "netadmin profile", + node: &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-XXX", + }, + }, + opts: &DebugOptions{ + Image: "busybox", + PullPolicy: corev1.PullIfNotPresent, + Profile: ProfileNetadmin, + }, + expected: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node-debugger-node-XXX-1", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "debugger", + Image: "busybox", + ImagePullPolicy: corev1.PullIfNotPresent, + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + VolumeMounts: nil, + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.Bool(true), + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"NET_ADMIN"}, + }, + }, + }, + }, + HostIPC: true, + HostNetwork: true, + HostPID: true, + NodeName: "node-XXX", + RestartPolicy: corev1.RestartPolicyNever, + Volumes: nil, + Tolerations: []corev1.Toleration{ + { + Operator: corev1.TolerationOpExists, + }, + }, + }, + }, + }, } { t.Run(tc.name, func(t *testing.T) { var err error - tc.opts.Applier, err = NewProfileApplier(ProfileLegacy) + tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile) if err != nil { - t.Fatalf("Fail to create legacy profile: %v", err) + t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err) } tc.opts.IOStreams = genericclioptions.NewTestIOStreamsDiscard() suffixCounter = 0 diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/debug/profiles_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/debug/profiles_test.go index 1e94a7a969b..6dfe3f994aa 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/debug/profiles_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/debug/profiles_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -518,6 +519,52 @@ func TestNetAdminProfile(t *testing.T) { }, }, }, + { + name: "debug by pod copy preserve existing capability", + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "app", Image: "appimage"}, + { + Name: "dbg", + Image: "dbgimage", + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"SYS_PTRACE"}, + }, + }, + }, + }, + }, + }, + containerName: "dbg", + target: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "app", Image: "appimage"}, + }, + }, + }, + expectPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "app", Image: "appimage"}, + { + Name: "dbg", + Image: "dbgimage", + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"SYS_PTRACE", "NET_ADMIN"}, + }, + }, + }, + }, + }, + }, + }, { name: "debug by node", pod: &corev1.Pod{ @@ -551,6 +598,48 @@ func TestNetAdminProfile(t *testing.T) { }, }, }, + { + name: "debug by node preserve existing capability", + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dbg", + Image: "dbgimage", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"SYS_PTRACE"}, + }, + }, + }, + }, + }, + }, + containerName: "dbg", + target: testNode, + expectPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod"}, + Spec: corev1.PodSpec{ + HostNetwork: true, + HostPID: true, + HostIPC: true, + Containers: []corev1.Container{ + { + Name: "dbg", + Image: "dbgimage", + SecurityContext: &corev1.SecurityContext{ + Privileged: pointer.BoolPtr(true), + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{"SYS_PTRACE", "NET_ADMIN"}, + }, + }, + }, + }, + }, + }, + }, } for _, test := range tests {