2945 lines
77 KiB
Go
2945 lines
77 KiB
Go
/*
|
|
Copyright 2020 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package debug
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/spf13/cobra"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
|
"k8s.io/utils/ptr"
|
|
)
|
|
|
|
func TestGenerateDebugContainer(t *testing.T) {
|
|
// Slightly less randomness for testing.
|
|
defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
|
|
var suffixCounter int
|
|
nameSuffixFunc = func(int) string {
|
|
suffixCounter++
|
|
return fmt.Sprint(suffixCounter)
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
opts *DebugOptions
|
|
pod *corev1.Pod
|
|
expected *corev1.EphemeralContainer
|
|
}{
|
|
{
|
|
name: "minimum fields",
|
|
opts: &DebugOptions{
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "namespace targeting",
|
|
opts: &DebugOptions{
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
TargetContainer: "myapp",
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
TargetContainerName: "myapp",
|
|
},
|
|
},
|
|
{
|
|
name: "debug args as container command",
|
|
opts: &DebugOptions{
|
|
Args: []string{"/bin/echo", "one", "two", "three"},
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger",
|
|
Command: []string{"/bin/echo", "one", "two", "three"},
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "debug args as container args",
|
|
opts: &DebugOptions{
|
|
ArgsOnly: true,
|
|
Container: "debugger",
|
|
Args: []string{"echo", "one", "two", "three"},
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger",
|
|
Args: []string{"echo", "one", "two", "three"},
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "random name generation",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "random name collision",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-2",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "pod with init containers",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
InitContainers: []corev1.Container{
|
|
{
|
|
Name: "init-container-1",
|
|
},
|
|
{
|
|
Name: "init-container-2",
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "pod with ephemeral containers",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
pod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
},
|
|
},
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "ephemeral-container-1",
|
|
},
|
|
},
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "ephemeral-container-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: "IfNotPresent",
|
|
TerminationMessagePolicy: "File",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "general profile",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileGeneral,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Add: []corev1.Capability{"SYS_PTRACE"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "baseline profile",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileBaseline,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "restricted profile",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileRestricted,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(true),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "netadmin profile",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileNetadmin,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "sysadmin profile",
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileSysadmin,
|
|
},
|
|
expected: &corev1.EphemeralContainer{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Privileged: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var err error
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("failed to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
suffixCounter = 0
|
|
if tc.pod == nil {
|
|
tc.pod = &corev1.Pod{}
|
|
}
|
|
|
|
_, debugContainer, err := tc.opts.generateDebugContainer(tc.pod)
|
|
if err != nil {
|
|
t.Fatalf("fail to generate debug container: %v", err)
|
|
}
|
|
if diff := cmp.Diff(tc.expected, debugContainer); diff != "" {
|
|
t.Error("unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGeneratePodCopyWithDebugContainer(t *testing.T) {
|
|
defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
|
|
var suffixCounter int
|
|
nameSuffixFunc = func(int) string {
|
|
suffixCounter++
|
|
return fmt.Sprint(suffixCounter)
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
opts *DebugOptions
|
|
havePod, wantPod *corev1.Pod
|
|
}{
|
|
{
|
|
name: "basic",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "same node",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
SameNode: true,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
Labels: map[string]string{
|
|
"app": "business",
|
|
},
|
|
},
|
|
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,
|
|
},
|
|
},
|
|
NodeName: "node-1",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "metadata stripping",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
Labels: map[string]string{
|
|
"app": "business",
|
|
},
|
|
Annotations: map[string]string{
|
|
"test": "test",
|
|
},
|
|
ResourceVersion: "1",
|
|
CreationTimestamp: metav1.Time{Time: time.Now()},
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
Annotations: map[string]string{
|
|
"test": "test",
|
|
},
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "add a debug container",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "customize envs",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Env: []corev1.EnvVar{{
|
|
Name: "TEST",
|
|
Value: "test",
|
|
}},
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
Env: []corev1.EnvVar{{
|
|
Name: "TEST",
|
|
Value: "test",
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "debug args as container command",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Args: []string{"/bin/echo", "one", "two", "three"},
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
Command: []string{"/bin/echo", "one", "two", "three"},
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "debug args as container command",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Args: []string{"one", "two", "three"},
|
|
ArgsOnly: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
Args: []string{"one", "two", "three"},
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "modify existing command to debug args",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Args: []string{"sleep", "1d"},
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Command: []string{"echo"},
|
|
Image: "app",
|
|
Args: []string{"one", "two", "three"},
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "app",
|
|
Command: []string{"sleep", "1d"},
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "random name",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
},
|
|
{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "random name collision",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
{
|
|
Name: "debugger-2",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "pod with probes",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
KeepLiveness: true,
|
|
KeepReadiness: true,
|
|
KeepStartup: true,
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
LivenessProbe: &corev1.Probe{},
|
|
ReadinessProbe: &corev1.Probe{},
|
|
StartupProbe: &corev1.Probe{},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "business",
|
|
LivenessProbe: &corev1.Probe{},
|
|
ReadinessProbe: &corev1.Probe{},
|
|
StartupProbe: &corev1.Probe{},
|
|
},
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "pod with init containers",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
InitContainers: []corev1.Container{
|
|
{
|
|
Name: "init-container-1",
|
|
},
|
|
{
|
|
Name: "init-container-2",
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
InitContainers: []corev1.Container{
|
|
{
|
|
Name: "init-container-1",
|
|
},
|
|
{
|
|
Name: "init-container-2",
|
|
},
|
|
},
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
{
|
|
Name: "debugger-2",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "pod with ephemeral containers",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
},
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "ephemeral-container-1",
|
|
},
|
|
},
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "ephemeral-container-2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger-1",
|
|
},
|
|
{
|
|
Name: "debugger-2",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "shared process namespace",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
ShareProcesses: true,
|
|
shareProcessedChanged: true,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "target",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
ImagePullPolicy: corev1.PullAlways,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
NodeName: "node-1",
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "debugger",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
},
|
|
},
|
|
ShareProcessNamespace: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Change image for a named container",
|
|
opts: &DebugOptions{
|
|
Args: []string{},
|
|
CopyTo: "myapp-copy",
|
|
Container: "app",
|
|
Image: "busybox",
|
|
TargetNames: []string{"myapp"},
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "myapp"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "myapp-copy"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "busybox"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Change image for a named container with set-image",
|
|
opts: &DebugOptions{
|
|
CopyTo: "myapp-copy",
|
|
Container: "app",
|
|
SetImages: map[string]string{"app": "busybox"},
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp-copy",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "busybox"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Change image for all containers with set-image",
|
|
opts: &DebugOptions{
|
|
CopyTo: "myapp-copy",
|
|
SetImages: map[string]string{"*": "busybox"},
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp-copy",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "busybox"},
|
|
{Name: "sidecar", Image: "busybox"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Change image for multiple containers with set-image",
|
|
opts: &DebugOptions{
|
|
CopyTo: "myapp-copy",
|
|
SetImages: map[string]string{"*": "busybox", "app": "app-debugger"},
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "myapp-copy",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "app-debugger"},
|
|
{Name: "sidecar", Image: "busybox"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Add interactive debug container minimal args",
|
|
opts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
Interactive: true,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
Stdin: true,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
TTY: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: add container and also mutate images",
|
|
opts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "debian",
|
|
Interactive: true,
|
|
Namespace: "default",
|
|
SetImages: map[string]string{
|
|
"app": "app:debug",
|
|
"sidecar": "sidecar:debug",
|
|
},
|
|
ShareProcesses: true,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
havePod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "mypod"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "appimage"},
|
|
{Name: "sidecar", Image: "sidecarimage"},
|
|
},
|
|
},
|
|
},
|
|
wantPod: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "my-debugger"},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{Name: "app", Image: "app:debug"},
|
|
{Name: "sidecar", Image: "sidecar:debug"},
|
|
{
|
|
Name: "debugger-1",
|
|
Image: "debian",
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
Stdin: true,
|
|
TTY: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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: ptr.To(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: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "baseline profile not share process when user explicitly disables it",
|
|
opts: &DebugOptions{
|
|
CopyTo: "debugger",
|
|
Container: "debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileBaseline,
|
|
ShareProcesses: false,
|
|
shareProcessedChanged: true,
|
|
},
|
|
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: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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{
|
|
RunAsNonRoot: ptr.To(true),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
|
|
},
|
|
},
|
|
},
|
|
ShareProcessNamespace: ptr.To(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", "NET_RAW"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ShareProcessNamespace: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var err error
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
suffixCounter = 0
|
|
|
|
if tc.havePod == nil {
|
|
tc.havePod = &corev1.Pod{}
|
|
}
|
|
gotPod, _, _ := tc.opts.generatePodCopyWithDebugContainer(tc.havePod)
|
|
if diff := cmp.Diff(tc.wantPod, gotPod); diff != "" {
|
|
t.Error("TestGeneratePodCopyWithDebugContainer: diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateNodeDebugPod(t *testing.T) {
|
|
defer func(old func(int) string) { nameSuffixFunc = old }(nameSuffixFunc)
|
|
var suffixCounter int
|
|
nameSuffixFunc = func(int) string {
|
|
suffixCounter++
|
|
return fmt.Sprint(suffixCounter)
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
node *corev1.Node
|
|
opts *DebugOptions
|
|
expected *corev1.Pod
|
|
}{
|
|
{
|
|
name: "minimum options",
|
|
node: &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-XXX",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
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: "debug args as container command",
|
|
node: &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-XXX",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
Args: []string{"/bin/echo", "one", "two", "three"},
|
|
Container: "custom-debugger",
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-debugger-node-XXX-1",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "custom-debugger",
|
|
Command: []string{"/bin/echo", "one", "two", "three"},
|
|
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: "debug args as container args",
|
|
node: &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-XXX",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
ArgsOnly: true,
|
|
Container: "custom-debugger",
|
|
Args: []string{"echo", "one", "two", "three"},
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileLegacy,
|
|
},
|
|
expected: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-debugger-node-XXX-1",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "custom-debugger",
|
|
Args: []string{"echo", "one", "two", "three"},
|
|
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: "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,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(true),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
|
|
},
|
|
},
|
|
},
|
|
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{
|
|
Capabilities: &corev1.Capabilities{
|
|
Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
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
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
suffixCounter = 0
|
|
|
|
pod, err := tc.opts.generateNodeDebugPod(tc.node)
|
|
if err != nil {
|
|
t.Fatalf("Fail to generate node debug pod: %v", err)
|
|
}
|
|
if diff := cmp.Diff(tc.expected, pod); diff != "" {
|
|
t.Error("unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateNodeDebugPodCustomProfile(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
node *corev1.Node
|
|
opts *DebugOptions
|
|
expected *corev1.Pod
|
|
}{
|
|
{
|
|
name: "baseline profile",
|
|
node: &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-XXX",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileBaseline,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-debugger-node-XXX-1",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
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,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-debugger-node-XXX-1",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(true),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
SeccompProfile: &corev1.SeccompProfile{Type: "RuntimeDefault"},
|
|
},
|
|
},
|
|
},
|
|
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,
|
|
CustomProfile: &corev1.Container{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "TEST_KEY",
|
|
Value: "TEST_VALUE",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "TEST_KEY",
|
|
Value: "TEST_VALUE",
|
|
},
|
|
},
|
|
VolumeMounts: nil,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: true,
|
|
HostNetwork: true,
|
|
HostPID: true,
|
|
NodeName: "node-XXX",
|
|
RestartPolicy: corev1.RestartPolicyNever,
|
|
Volumes: nil,
|
|
Tolerations: []corev1.Toleration{
|
|
{
|
|
Operator: corev1.TolerationOpExists,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "sysadmin profile",
|
|
node: &corev1.Node{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "node-XXX",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileSysadmin,
|
|
CustomProfile: &corev1.Container{
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "TEST_KEY",
|
|
Value: "TEST_VALUE",
|
|
},
|
|
},
|
|
VolumeMounts: []corev1.VolumeMount{
|
|
{
|
|
Name: "host-root",
|
|
ReadOnly: true,
|
|
MountPath: "/host",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "debugger",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
Env: []corev1.EnvVar{
|
|
{
|
|
Name: "TEST_KEY",
|
|
Value: "TEST_VALUE",
|
|
},
|
|
},
|
|
VolumeMounts: []corev1.VolumeMount{
|
|
{
|
|
Name: "host-root",
|
|
ReadOnly: true,
|
|
MountPath: "/host",
|
|
},
|
|
},
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Privileged: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var err error
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
|
|
pod, err := tc.opts.generateNodeDebugPod(tc.node)
|
|
if err != nil {
|
|
t.Fatalf("Fail to generate node debug pod: %v", err)
|
|
}
|
|
tc.expected.Name = pod.Name
|
|
if diff := cmp.Diff(tc.expected, pod); diff != "" {
|
|
t.Error("unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateCopyDebugPodCustomProfile(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
copyPod *corev1.Pod
|
|
opts *DebugOptions
|
|
expected *corev1.Pod
|
|
}{
|
|
{
|
|
name: "baseline profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileBaseline,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
Containers: []corev1.Container{
|
|
{
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
ShareProcessNamespace: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "restricted profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileRestricted,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
Containers: []corev1.Container{
|
|
{
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
SeccompProfile: &corev1.SeccompProfile{
|
|
Type: corev1.SeccompProfileTypeRuntimeDefault,
|
|
LocalhostProfile: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
ShareProcessNamespace: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "sysadmin profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileRestricted,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
Containers: []corev1.Container{
|
|
{
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
SeccompProfile: &corev1.SeccompProfile{
|
|
Type: corev1.SeccompProfileTypeRuntimeDefault,
|
|
LocalhostProfile: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
ShareProcessNamespace: ptr.To(true),
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var err error
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
|
|
pod, dc, err := tc.opts.generatePodCopyWithDebugContainer(tc.copyPod)
|
|
if err != nil {
|
|
t.Fatalf("Fail to generate node debug pod: %v", err)
|
|
}
|
|
tc.expected.Spec.Containers[0].Name = dc
|
|
if diff := cmp.Diff(tc.expected, pod); diff != "" {
|
|
t.Error("unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateEphemeralDebugPodCustomProfile(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
copyPod *corev1.Pod
|
|
opts *DebugOptions
|
|
expected *corev1.Pod
|
|
}{
|
|
{
|
|
name: "baseline profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileBaseline,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "restricted profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileRestricted,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
SeccompProfile: &corev1.SeccompProfile{
|
|
Type: corev1.SeccompProfileTypeRuntimeDefault,
|
|
LocalhostProfile: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "sysadmin profile",
|
|
copyPod: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
},
|
|
},
|
|
opts: &DebugOptions{
|
|
SameNode: true,
|
|
Image: "busybox",
|
|
PullPolicy: corev1.PullIfNotPresent,
|
|
Profile: ProfileRestricted,
|
|
CustomProfile: &corev1.Container{
|
|
ImagePullPolicy: corev1.PullNever,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
RunAsNonRoot: ptr.To(false),
|
|
},
|
|
},
|
|
},
|
|
expected: &corev1.Pod{
|
|
Spec: corev1.PodSpec{
|
|
ServiceAccountName: "test",
|
|
NodeName: "test-node",
|
|
EphemeralContainers: []corev1.EphemeralContainer{
|
|
{
|
|
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
|
Name: "debugger-1",
|
|
Image: "busybox",
|
|
ImagePullPolicy: corev1.PullNever,
|
|
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
|
VolumeMounts: nil,
|
|
Stdin: true,
|
|
TTY: false,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
AllowPrivilegeEscalation: ptr.To(false),
|
|
RunAsNonRoot: ptr.To(false),
|
|
Capabilities: &corev1.Capabilities{
|
|
Drop: []corev1.Capability{"ALL"},
|
|
},
|
|
SeccompProfile: &corev1.SeccompProfile{
|
|
Type: corev1.SeccompProfileTypeRuntimeDefault,
|
|
LocalhostProfile: nil,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
HostIPC: false,
|
|
HostNetwork: false,
|
|
HostPID: false,
|
|
Volumes: nil,
|
|
},
|
|
},
|
|
},
|
|
} {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var err error
|
|
kflags := KeepFlags{
|
|
Labels: tc.opts.KeepLabels,
|
|
Annotations: tc.opts.KeepAnnotations,
|
|
Liveness: tc.opts.KeepLiveness,
|
|
Readiness: tc.opts.KeepReadiness,
|
|
Startup: tc.opts.KeepStartup,
|
|
InitContainers: tc.opts.KeepInitContainers,
|
|
}
|
|
tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile, kflags)
|
|
if err != nil {
|
|
t.Fatalf("Fail to create profile applier: %s: %v", tc.opts.Profile, err)
|
|
}
|
|
tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
|
|
|
|
pod, ec, err := tc.opts.generateDebugContainer(tc.copyPod)
|
|
if err != nil {
|
|
t.Fatalf("Fail to generate node debug pod: %v", err)
|
|
}
|
|
tc.expected.Spec.EphemeralContainers[0].Name = ec.Name
|
|
if diff := cmp.Diff(tc.expected, pod); diff != "" {
|
|
t.Error("unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompleteAndValidate(t *testing.T) {
|
|
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
|
ioStreams, _, _, _ := genericiooptions.NewTestIOStreams()
|
|
cmpFilter := cmp.FilterPath(func(p cmp.Path) bool {
|
|
switch p.String() {
|
|
// IOStreams contains unexported fields
|
|
case "IOStreams", "Applier":
|
|
return true
|
|
}
|
|
return false
|
|
}, cmp.Ignore())
|
|
|
|
tests := []struct {
|
|
name, args string
|
|
wantOpts *DebugOptions
|
|
wantError bool
|
|
}{
|
|
{
|
|
name: "No targets",
|
|
args: "--image=image",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Invalid environment variables",
|
|
args: "--image=busybox --env=FOO mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Invalid image name",
|
|
args: "--image=image:label@deadbeef mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Invalid pull policy",
|
|
args: "--image=image --image-pull-policy=whenever-you-feel-like-it",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "TTY without stdin",
|
|
args: "--image=image --tty",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Set image pull policy",
|
|
args: "--image=busybox --image-pull-policy=Always mypod",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
PullPolicy: corev1.PullPolicy("Always"),
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Multiple targets",
|
|
args: "--image=busybox mypod1 mypod2",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod1", "mypod2"},
|
|
},
|
|
},
|
|
{
|
|
name: "Arguments with dash",
|
|
args: "--image=busybox mypod1 mypod2 -- echo 1 2",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{"echo", "1", "2"},
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod1", "mypod2"},
|
|
},
|
|
},
|
|
{
|
|
name: "Interactive no attach",
|
|
args: "-ti --image=busybox --attach=false mypod",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: false,
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Interactive: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Set environment variables",
|
|
args: "--image=busybox --env=FOO=BAR mypod",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Env: []corev1.EnvVar{{Name: "FOO", Value: "BAR"}},
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Ephemeral container: interactive session minimal args",
|
|
args: "mypod -it --image=busybox",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
Image: "busybox",
|
|
Interactive: true,
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Ephemeral container: non-interactive debugger with image and name",
|
|
args: "--image=myproj/debug-tools --image-pull-policy=Always -c debugger mypod",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Container: "debugger",
|
|
Image: "myproj/debug-tools",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
PullPolicy: corev1.PullPolicy("Always"),
|
|
Profile: ProfileLegacy,
|
|
ShareProcesses: true,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Ephemeral container: no image specified",
|
|
args: "mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Ephemeral container: no image but args",
|
|
args: "mypod -- echo 1 2",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Ephemeral container: replace not allowed",
|
|
args: "--replace --image=busybox mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Ephemeral container: same-node not allowed",
|
|
args: "--same-node --image=busybox mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Ephemeral container: incompatible with --set-image",
|
|
args: "--set-image=*=busybox mypod",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Pod copy: interactive debug container minimal args",
|
|
args: "mypod -it --image=busybox --copy-to=my-debugger",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
Interactive: true,
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: non-interactive with debug container, image name and command",
|
|
args: "mypod --image=busybox --container=my-container --copy-to=my-debugger -- sleep 1d",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{"sleep", "1d"},
|
|
Container: "my-container",
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: explicit attach",
|
|
args: "mypod --image=busybox --copy-to=my-debugger --attach -- sleep 1d",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{"sleep", "1d"},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: replace single image of existing container",
|
|
args: "mypod --image=busybox --container=my-container --copy-to=my-debugger",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Container: "my-container",
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: mutate existing container images",
|
|
args: "mypod --set-image=*=busybox,app=app-debugger --copy-to=my-debugger",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
CopyTo: "my-debugger",
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
SetImages: map[string]string{
|
|
"*": "busybox",
|
|
"app": "app-debugger",
|
|
},
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: add container and also mutate images",
|
|
args: "mypod -it --copy-to=my-debugger --image=debian --set-image=app=app:debug,sidecar=sidecar:debug",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "debian",
|
|
Interactive: true,
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
SetImages: map[string]string{
|
|
"app": "app:debug",
|
|
"sidecar": "sidecar:debug",
|
|
},
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: change command",
|
|
args: "mypod -it --copy-to=my-debugger --container=mycontainer -- sh",
|
|
wantOpts: &DebugOptions{
|
|
Attach: true,
|
|
Args: []string{"sh"},
|
|
Container: "mycontainer",
|
|
CopyTo: "my-debugger",
|
|
Interactive: true,
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: change keep options from defaults",
|
|
args: "mypod -it --image=busybox --copy-to=my-debugger --keep-labels=true --keep-annotations=true --keep-liveness=true --keep-readiness=true --keep-startup=true --keep-init-containers=false",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
CopyTo: "my-debugger",
|
|
Image: "busybox",
|
|
Interactive: true,
|
|
KeepLabels: true,
|
|
KeepAnnotations: true,
|
|
KeepLiveness: true,
|
|
KeepReadiness: true,
|
|
KeepStartup: true,
|
|
KeepInitContainers: false,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"mypod"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Pod copy: no image specified",
|
|
args: "mypod -it --copy-to=my-debugger",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Pod copy: args but no image specified",
|
|
args: "mypod --copy-to=my-debugger -- echo milo",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Pod copy: --target not allowed",
|
|
args: "mypod --target --image=busybox --copy-to=my-debugger",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Pod copy: invalid --set-image",
|
|
args: "mypod --set-image=*=SUPERGOODIMAGE#1!!!! --copy-to=my-debugger",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Pod copy: specifying attach without existing or newly created container",
|
|
args: "mypod --set-image=*=busybox --copy-to=my-debugger --attach",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Node: interactive session minimal args",
|
|
args: "node/mynode -it --image=busybox",
|
|
wantOpts: &DebugOptions{
|
|
Args: []string{},
|
|
Attach: true,
|
|
Image: "busybox",
|
|
Interactive: true,
|
|
KeepInitContainers: true,
|
|
Namespace: "test",
|
|
ShareProcesses: true,
|
|
Profile: ProfileLegacy,
|
|
TargetNames: []string{"node/mynode"},
|
|
TTY: true,
|
|
},
|
|
},
|
|
{
|
|
name: "Node: no image specified",
|
|
args: "node/mynode -it",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Node: --replace not allowed",
|
|
args: "--image=busybox --replace node/mynode",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Node: --same-node not allowed",
|
|
args: "--image=busybox --same-node node/mynode",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Node: --set-image not allowed",
|
|
args: "--image=busybox --set-image=*=busybox node/mynode",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "Node: --target not allowed",
|
|
args: "node/mynode --target --image=busybox",
|
|
wantError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
opts := NewDebugOptions(ioStreams)
|
|
var gotError error
|
|
cmd := &cobra.Command{
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
gotError = opts.Complete(tf, cmd, args)
|
|
if gotError != nil {
|
|
return
|
|
}
|
|
gotError = opts.Validate()
|
|
},
|
|
}
|
|
cmd.SetArgs(strings.Split(tc.args, " "))
|
|
opts.AddFlags(cmd)
|
|
|
|
cmdError := cmd.Execute()
|
|
|
|
if tc.wantError {
|
|
if cmdError != nil || gotError != nil {
|
|
return
|
|
}
|
|
t.Fatalf("CompleteAndValidate got nil errors but wantError: %v", tc.wantError)
|
|
} else if cmdError != nil {
|
|
t.Fatalf("cmd.Execute got error '%v' executing test cobra.Command, wantError: %v", cmdError, tc.wantError)
|
|
} else if gotError != nil {
|
|
t.Fatalf("CompleteAndValidate got error: '%v', wantError: %v", gotError, tc.wantError)
|
|
}
|
|
|
|
if diff := cmp.Diff(tc.wantOpts, opts, cmpFilter, cmpopts.IgnoreFields(DebugOptions{},
|
|
"attachChanged", "shareProcessedChanged", "podClient", "WarningPrinter", "Applier", "explicitNamespace", "Builder", "AttachFunc")); diff != "" {
|
|
t.Error("CompleteAndValidate unexpected diff in generated object: (-want +got):\n", diff)
|
|
}
|
|
})
|
|
}
|
|
}
|