kubectl: add unit tests for kubectl debug profiles

Unit test netadmin profile preserves existing capabilities.
Unit test debug profiles in TestGenerateNodeDebugPod
Unit test debug profiles in TestGeneratePodCopyWithDebugContainer
Organize Go imports in unit tests

Signed-off-by: Will Daly <widaly@microsoft.com>

Kubernetes-commit: 21e8d2958190e9813fe1122d1e7a91e8143a5193
This commit is contained in:
Will Daly 2023-02-16 06:38:12 -08:00 committed by Kubernetes Publisher
parent 060672725b
commit bfae4f6bfd
2 changed files with 462 additions and 7 deletions

View File

@ -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

View File

@ -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 {