Merge pull request #120346 from ardaguclu/custom-debug-profile
kubectl debug: Add custom debug profiles on top of static profiles
This commit is contained in:
		@@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/distribution/reference"
 | 
			
		||||
@@ -106,29 +107,33 @@ var (
 | 
			
		||||
 | 
			
		||||
var nameSuffixFunc = utilrand.String
 | 
			
		||||
 | 
			
		||||
type DebugAttachFunc func(ctx context.Context, restClientGetter genericclioptions.RESTClientGetter, cmdPath string, ns, podName, containerName string) error
 | 
			
		||||
 | 
			
		||||
// DebugOptions holds the options for an invocation of kubectl debug.
 | 
			
		||||
type DebugOptions struct {
 | 
			
		||||
	Args            []string
 | 
			
		||||
	ArgsOnly        bool
 | 
			
		||||
	Attach          bool
 | 
			
		||||
	AttachFunc      func(ctx context.Context, restClientGetter genericclioptions.RESTClientGetter, cmdPath string, ns, podName, containerName string) error
 | 
			
		||||
	Container       string
 | 
			
		||||
	CopyTo          string
 | 
			
		||||
	Replace         bool
 | 
			
		||||
	Env             []corev1.EnvVar
 | 
			
		||||
	Image           string
 | 
			
		||||
	Interactive     bool
 | 
			
		||||
	Namespace       string
 | 
			
		||||
	TargetNames     []string
 | 
			
		||||
	PullPolicy      corev1.PullPolicy
 | 
			
		||||
	Quiet           bool
 | 
			
		||||
	SameNode        bool
 | 
			
		||||
	SetImages       map[string]string
 | 
			
		||||
	ShareProcesses  bool
 | 
			
		||||
	TargetContainer string
 | 
			
		||||
	TTY             bool
 | 
			
		||||
	Profile         string
 | 
			
		||||
	Applier         ProfileApplier
 | 
			
		||||
	Args              []string
 | 
			
		||||
	ArgsOnly          bool
 | 
			
		||||
	Attach            bool
 | 
			
		||||
	AttachFunc        DebugAttachFunc
 | 
			
		||||
	Container         string
 | 
			
		||||
	CopyTo            string
 | 
			
		||||
	Replace           bool
 | 
			
		||||
	Env               []corev1.EnvVar
 | 
			
		||||
	Image             string
 | 
			
		||||
	Interactive       bool
 | 
			
		||||
	Namespace         string
 | 
			
		||||
	TargetNames       []string
 | 
			
		||||
	PullPolicy        corev1.PullPolicy
 | 
			
		||||
	Quiet             bool
 | 
			
		||||
	SameNode          bool
 | 
			
		||||
	SetImages         map[string]string
 | 
			
		||||
	ShareProcesses    bool
 | 
			
		||||
	TargetContainer   string
 | 
			
		||||
	TTY               bool
 | 
			
		||||
	Profile           string
 | 
			
		||||
	CustomProfileFile string
 | 
			
		||||
	CustomProfile     *corev1.Container
 | 
			
		||||
	Applier           ProfileApplier
 | 
			
		||||
 | 
			
		||||
	explicitNamespace     bool
 | 
			
		||||
	attachChanged         bool
 | 
			
		||||
@@ -193,6 +198,9 @@ func (o *DebugOptions) AddFlags(cmd *cobra.Command) {
 | 
			
		||||
	cmd.Flags().StringVar(&o.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name."))
 | 
			
		||||
	cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, i18n.T("Allocate a TTY for the debugging container."))
 | 
			
		||||
	cmd.Flags().StringVar(&o.Profile, "profile", ProfileLegacy, i18n.T(`Options are "legacy", "general", "baseline", "netadmin", "restricted" or "sysadmin".`))
 | 
			
		||||
	if cmdutil.DebugCustomProfile.IsEnabled() {
 | 
			
		||||
		cmd.Flags().StringVar(&o.CustomProfileFile, "custom", o.CustomProfileFile, i18n.T("Path to a JSON file containing a partial container spec to customize built-in debug profiles."))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Complete finishes run-time initialization of debug.DebugOptions.
 | 
			
		||||
@@ -256,6 +264,18 @@ func (o *DebugOptions) Complete(restClientGetter genericclioptions.RESTClientGet
 | 
			
		||||
		o.Applier = applier
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.CustomProfileFile != "" {
 | 
			
		||||
		customProfileBytes, err := os.ReadFile(o.CustomProfileFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("must pass a container spec json file for custom profile: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = json.Unmarshal(customProfileBytes, &o.CustomProfile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("%s does not contain a valid container spec: %w", o.CustomProfileFile, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clientConfig, err := restClientGetter.ToRESTConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -348,6 +368,12 @@ func (o *DebugOptions) Validate() error {
 | 
			
		||||
		return fmt.Errorf("WarningPrinter can not be used without initialization")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.CustomProfile != nil {
 | 
			
		||||
		if o.CustomProfile.Name != "" || len(o.CustomProfile.Command) > 0 || o.CustomProfile.Image != "" || o.CustomProfile.Lifecycle != nil || len(o.CustomProfile.VolumeDevices) > 0 {
 | 
			
		||||
			return fmt.Errorf("name, command, image, lifecycle and volume devices are not modifiable via custom profile")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -467,6 +493,90 @@ func (o *DebugOptions) debugByEphemeralContainer(ctx context.Context, pod *corev
 | 
			
		||||
	return result, debugContainer.Name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// applyCustomProfile applies given partial container json file on to the profile
 | 
			
		||||
// incorporated debug pod.
 | 
			
		||||
func (o *DebugOptions) applyCustomProfile(debugPod *corev1.Pod, containerName string) error {
 | 
			
		||||
	o.CustomProfile.Name = containerName
 | 
			
		||||
	customJS, err := json.Marshal(o.CustomProfile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to marshall custom profile: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var index int
 | 
			
		||||
	found := false
 | 
			
		||||
	for i, val := range debugPod.Spec.Containers {
 | 
			
		||||
		if val.Name == containerName {
 | 
			
		||||
			index = i
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !found {
 | 
			
		||||
		return fmt.Errorf("unable to find the %s container in the pod %s", containerName, debugPod.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var debugContainerJS []byte
 | 
			
		||||
	debugContainerJS, err = json.Marshal(debugPod.Spec.Containers[index])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to marshall container: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	patchedContainer, err := strategicpatch.StrategicMergePatch(debugContainerJS, customJS, corev1.Container{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error creating three way patch to add debug container: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(patchedContainer, &debugPod.Spec.Containers[index])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to unmarshall patched container to container: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// applyCustomProfileEphemeral applies given partial container json file on to the profile
 | 
			
		||||
// incorporated ephemeral container of the pod.
 | 
			
		||||
func (o *DebugOptions) applyCustomProfileEphemeral(debugPod *corev1.Pod, containerName string) error {
 | 
			
		||||
	o.CustomProfile.Name = containerName
 | 
			
		||||
	customJS, err := json.Marshal(o.CustomProfile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to marshall custom profile: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var index int
 | 
			
		||||
	found := false
 | 
			
		||||
	for i, val := range debugPod.Spec.EphemeralContainers {
 | 
			
		||||
		if val.Name == containerName {
 | 
			
		||||
			index = i
 | 
			
		||||
			found = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !found {
 | 
			
		||||
		return fmt.Errorf("unable to find the %s ephemeral container in the pod %s", containerName, debugPod.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var debugContainerJS []byte
 | 
			
		||||
	debugContainerJS, err = json.Marshal(debugPod.Spec.EphemeralContainers[index])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to marshall ephemeral container:%w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	patchedContainer, err := strategicpatch.StrategicMergePatch(debugContainerJS, customJS, corev1.Container{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error creating three way patch to add debug container: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(patchedContainer, &debugPod.Spec.EphemeralContainers[index])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to unmarshall patched container to ephemeral container: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// debugByCopy runs a copy of the target Pod with a debug container added or an original container modified
 | 
			
		||||
func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev1.Pod, string, error) {
 | 
			
		||||
	copied, dc, err := o.generatePodCopyWithDebugContainer(pod)
 | 
			
		||||
@@ -515,6 +625,13 @@ func (o *DebugOptions) generateDebugContainer(pod *corev1.Pod) (*corev1.Pod, *co
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.CustomProfile != nil {
 | 
			
		||||
		err := o.applyCustomProfileEphemeral(copied, ec.Name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ec = &copied.Spec.EphemeralContainers[len(copied.Spec.EphemeralContainers)-1]
 | 
			
		||||
 | 
			
		||||
	return copied, ec, nil
 | 
			
		||||
@@ -574,6 +691,13 @@ func (o *DebugOptions) generateNodeDebugPod(node *corev1.Node) (*corev1.Pod, err
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.CustomProfile != nil {
 | 
			
		||||
		err := o.applyCustomProfile(p, cn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -656,6 +780,13 @@ func (o *DebugOptions) generatePodCopyWithDebugContainer(pod *corev1.Pod) (*core
 | 
			
		||||
		return nil, "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if o.CustomProfile != nil {
 | 
			
		||||
		err = o.applyCustomProfile(copied, name)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return copied, name, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,8 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	"github.com/google/go-cmp/cmp/cmpopts"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
@@ -1755,6 +1757,689 @@ func TestGenerateNodeDebugPod(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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: pointer.Bool(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: pointer.Bool(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: pointer.Bool(true),
 | 
			
		||||
								Capabilities: &corev1.Capabilities{
 | 
			
		||||
									Drop: []corev1.Capability{"ALL"},
 | 
			
		||||
								},
 | 
			
		||||
								AllowPrivilegeEscalation: pointer.Bool(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: pointer.Bool(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) {
 | 
			
		||||
			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
 | 
			
		||||
				var err error
 | 
			
		||||
				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
 | 
			
		||||
				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: pointer.Bool(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: pointer.Bool(false),
 | 
			
		||||
								Capabilities: &corev1.Capabilities{
 | 
			
		||||
									Drop: []corev1.Capability{"ALL"},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					HostIPC:               false,
 | 
			
		||||
					HostNetwork:           false,
 | 
			
		||||
					HostPID:               false,
 | 
			
		||||
					Volumes:               nil,
 | 
			
		||||
					ShareProcessNamespace: pointer.Bool(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: pointer.Bool(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: pointer.Bool(false),
 | 
			
		||||
								RunAsNonRoot:             pointer.Bool(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: pointer.Bool(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: pointer.Bool(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: pointer.Bool(false),
 | 
			
		||||
								RunAsNonRoot:             pointer.Bool(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: pointer.Bool(true),
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
 | 
			
		||||
				var err error
 | 
			
		||||
				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
 | 
			
		||||
				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: pointer.Bool(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: pointer.Bool(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: pointer.Bool(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: pointer.Bool(false),
 | 
			
		||||
									RunAsNonRoot:             pointer.Bool(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: pointer.Bool(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: pointer.Bool(false),
 | 
			
		||||
									RunAsNonRoot:             pointer.Bool(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) {
 | 
			
		||||
			cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.DebugCustomProfile}, t, func(t *testing.T) {
 | 
			
		||||
				var err error
 | 
			
		||||
				tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile)
 | 
			
		||||
				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()
 | 
			
		||||
 
 | 
			
		||||
@@ -431,6 +431,7 @@ const (
 | 
			
		||||
	OpenAPIV3Patch          FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
 | 
			
		||||
	RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
 | 
			
		||||
	PortForwardWebsockets   FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
 | 
			
		||||
	DebugCustomProfile      FeatureGate = "KUBECTL_DEBUG_CUSTOM_PROFILE"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsEnabled returns true iff environment variable is set to true.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user