Add keep options
This commit is contained in:
		
							
								
								
									
										24
									
								
								hack/testdata/pod-with-metadata-and-probes.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								hack/testdata/pod-with-metadata-and-probes.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| apiVersion: v1 | ||||
| kind: Pod | ||||
| metadata: | ||||
|   annotations: | ||||
|     test: test | ||||
|   labels: | ||||
|     run: target | ||||
|   name: target | ||||
| spec: | ||||
|   containers: | ||||
|   - image: registry.k8s.io/nginx:1.7.9 | ||||
|     name: target | ||||
|     readinessProbe: | ||||
|       exec: | ||||
|         command: ["/bin/sh", "-c", "cat probe"] | ||||
|     livenessProbe: | ||||
|       exec: | ||||
|         command: ["/bin/sh", "-c", "cat probe"] | ||||
|     startupProbe: | ||||
|       exec: | ||||
|         command: ["/bin/sh", "-c", "cat probe"] | ||||
|   initContainers: | ||||
|   - image: busybox | ||||
|     name: init | ||||
| @@ -111,29 +111,35 @@ type DebugAttachFunc func(ctx context.Context, restClientGetter genericclioption | ||||
|  | ||||
| // DebugOptions holds the options for an invocation of kubectl debug. | ||||
| type DebugOptions struct { | ||||
| 	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 | ||||
| 	Args               []string | ||||
| 	ArgsOnly           bool | ||||
| 	Attach             bool | ||||
| 	AttachFunc         DebugAttachFunc | ||||
| 	Container          string | ||||
| 	CopyTo             string | ||||
| 	Replace            bool | ||||
| 	Env                []corev1.EnvVar | ||||
| 	Image              string | ||||
| 	Interactive        bool | ||||
| 	KeepLabels         bool | ||||
| 	KeepAnnotations    bool | ||||
| 	KeepLiveness       bool | ||||
| 	KeepReadiness      bool | ||||
| 	KeepStartup        bool | ||||
| 	KeepInitContainers 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 | ||||
| @@ -151,10 +157,11 @@ type DebugOptions struct { | ||||
| // NewDebugOptions returns a DebugOptions initialized with default values. | ||||
| func NewDebugOptions(streams genericiooptions.IOStreams) *DebugOptions { | ||||
| 	return &DebugOptions{ | ||||
| 		Args:           []string{}, | ||||
| 		IOStreams:      streams, | ||||
| 		TargetNames:    []string{}, | ||||
| 		ShareProcesses: true, | ||||
| 		Args:               []string{}, | ||||
| 		IOStreams:          streams, | ||||
| 		KeepInitContainers: true, | ||||
| 		TargetNames:        []string{}, | ||||
| 		ShareProcesses:     true, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -189,6 +196,12 @@ func (o *DebugOptions) AddFlags(cmd *cobra.Command) { | ||||
| 	cmd.Flags().BoolVar(&o.Replace, "replace", o.Replace, i18n.T("When used with '--copy-to', delete the original Pod.")) | ||||
| 	cmd.Flags().StringToString("env", nil, i18n.T("Environment variables to set in the container.")) | ||||
| 	cmd.Flags().StringVar(&o.Image, "image", o.Image, i18n.T("Container image to use for debug container.")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepLabels, "keep-labels", o.KeepLabels, i18n.T("If true, keep the original pod labels.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepAnnotations, "keep-annotations", o.KeepAnnotations, i18n.T("If true, keep the original pod annotations.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepLiveness, "keep-liveness", o.KeepLiveness, i18n.T("If true, keep the original pod liveness probes.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepReadiness, "keep-readiness", o.KeepReadiness, i18n.T("If true, keep the original pod readiness probes.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepStartup, "keep-startup", o.KeepStartup, i18n.T("If true, keep the original startup probes.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().BoolVar(&o.KeepInitContainers, "keep-init-containers", o.KeepInitContainers, i18n.T("Run the init containers for the pod. Defaults to true.(This flag only works when used with '--copy-to')")) | ||||
| 	cmd.Flags().StringToStringVar(&o.SetImages, "set-image", o.SetImages, i18n.T("When used with '--copy-to', a list of name=image pairs for changing container images, similar to how 'kubectl set image' works.")) | ||||
| 	cmd.Flags().String("image-pull-policy", "", i18n.T("The image pull policy for the container. If left empty, this value will not be specified by the client and defaulted by the server.")) | ||||
| 	cmd.Flags().BoolVarP(&o.Interactive, "stdin", "i", o.Interactive, i18n.T("Keep stdin open on the container(s) in the pod, even if nothing is attached.")) | ||||
| @@ -257,7 +270,15 @@ func (o *DebugOptions) Complete(restClientGetter genericclioptions.RESTClientGet | ||||
| 	} | ||||
|  | ||||
| 	if o.Applier == nil { | ||||
| 		applier, err := NewProfileApplier(o.Profile) | ||||
| 		kflags := KeepFlags{ | ||||
| 			Labels:         o.KeepLabels, | ||||
| 			Annotations:    o.KeepAnnotations, | ||||
| 			Liveness:       o.KeepLiveness, | ||||
| 			Readiness:      o.KeepReadiness, | ||||
| 			Startup:        o.KeepStartup, | ||||
| 			InitContainers: o.KeepInitContainers, | ||||
| 		} | ||||
| 		applier, err := NewProfileApplier(o.Profile, kflags) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -708,6 +729,7 @@ func (o *DebugOptions) generatePodCopyWithDebugContainer(pod *corev1.Pod) (*core | ||||
| 			Name:        o.CopyTo, | ||||
| 			Namespace:   pod.Namespace, | ||||
| 			Annotations: pod.Annotations, | ||||
| 			Labels:      pod.Labels, | ||||
| 		}, | ||||
| 		Spec: *pod.Spec.DeepCopy(), | ||||
| 	} | ||||
|   | ||||
| @@ -339,18 +339,24 @@ func TestGenerateDebugContainer(t *testing.T) { | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			tc.opts.IOStreams = genericiooptions.NewTestIOStreamsDiscard() | ||||
| 			suffixCounter = 0 | ||||
|  | ||||
| 			if tc.pod == nil { | ||||
| 				tc.pod = &corev1.Pod{} | ||||
| 			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, | ||||
| 			} | ||||
|  | ||||
| 			applier, err := NewProfileApplier(tc.opts.Profile) | ||||
| 			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.Applier = applier | ||||
| 			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 { | ||||
| @@ -426,6 +432,9 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { | ||||
| 			havePod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "target", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "business", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| @@ -792,13 +801,63 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			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", | ||||
| 				PullPolicy: corev1.PullIfNotPresent, | ||||
| 				Profile:    ProfileLegacy, | ||||
| 				CopyTo:             "debugger", | ||||
| 				Image:              "busybox", | ||||
| 				KeepInitContainers: true, | ||||
| 				PullPolicy:         corev1.PullIfNotPresent, | ||||
| 				Profile:            ProfileLegacy, | ||||
| 			}, | ||||
| 			havePod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| @@ -1356,7 +1415,15 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { | ||||
| 	} { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			var err error | ||||
| 			tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile) | ||||
| 			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) | ||||
| 			} | ||||
| @@ -1739,7 +1806,15 @@ func TestGenerateNodeDebugPod(t *testing.T) { | ||||
| 	} { | ||||
| 		t.Run(tc.name, func(t *testing.T) { | ||||
| 			var err error | ||||
| 			tc.opts.Applier, err = NewProfileApplier(tc.opts.Profile) | ||||
| 			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) | ||||
| 			} | ||||
| @@ -2015,7 +2090,15 @@ func TestGenerateNodeDebugPodCustomProfile(t *testing.T) { | ||||
| 		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) | ||||
| 				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) | ||||
| 				} | ||||
| @@ -2215,7 +2298,15 @@ func TestGenerateCopyDebugPodCustomProfile(t *testing.T) { | ||||
| 		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) | ||||
| 				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) | ||||
| 				} | ||||
| @@ -2421,7 +2512,15 @@ func TestGenerateEphemeralDebugPodCustomProfile(t *testing.T) { | ||||
| 		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) | ||||
| 				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) | ||||
| 				} | ||||
| @@ -2486,94 +2585,101 @@ func TestCompleteAndValidate(t *testing.T) { | ||||
| 			name: "Set image pull policy", | ||||
| 			args: "--image=busybox --image-pull-policy=Always mypod", | ||||
| 			wantOpts: &DebugOptions{ | ||||
| 				Args:           []string{}, | ||||
| 				Image:          "busybox", | ||||
| 				Namespace:      "test", | ||||
| 				PullPolicy:     corev1.PullPolicy("Always"), | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod1", "mypod2"}, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod1", "mypod2"}, | ||||
| 				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", | ||||
| 				Interactive:    true, | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				TTY:            true, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				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, | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				TTY:            true, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				PullPolicy:     corev1.PullPolicy("Always"), | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				ShareProcesses: true, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				Args:               []string{}, | ||||
| 				Container:          "debugger", | ||||
| 				Image:              "myproj/debug-tools", | ||||
| 				KeepInitContainers: true, | ||||
| 				Namespace:          "test", | ||||
| 				PullPolicy:         corev1.PullPolicy("Always"), | ||||
| 				Profile:            ProfileLegacy, | ||||
| 				ShareProcesses:     true, | ||||
| 				TargetNames:        []string{"mypod"}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -2605,67 +2711,72 @@ func TestCompleteAndValidate(t *testing.T) { | ||||
| 			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, | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				TTY:            true, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				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", | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				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", | ||||
| 				Namespace: "test", | ||||
| 				Args:               []string{}, | ||||
| 				CopyTo:             "my-debugger", | ||||
| 				KeepInitContainers: true, | ||||
| 				Namespace:          "test", | ||||
| 				SetImages: map[string]string{ | ||||
| 					"*":   "busybox", | ||||
| 					"app": "app-debugger", | ||||
| @@ -2679,12 +2790,13 @@ func TestCompleteAndValidate(t *testing.T) { | ||||
| 			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, | ||||
| 				Namespace:   "test", | ||||
| 				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", | ||||
| @@ -2699,16 +2811,39 @@ func TestCompleteAndValidate(t *testing.T) { | ||||
| 			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, | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"mypod"}, | ||||
| 				TTY:            true, | ||||
| 				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, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -2740,15 +2875,16 @@ func TestCompleteAndValidate(t *testing.T) { | ||||
| 			name: "Node: interactive session minimal args", | ||||
| 			args: "node/mynode -it --image=busybox", | ||||
| 			wantOpts: &DebugOptions{ | ||||
| 				Args:           []string{}, | ||||
| 				Attach:         true, | ||||
| 				Image:          "busybox", | ||||
| 				Interactive:    true, | ||||
| 				Namespace:      "test", | ||||
| 				ShareProcesses: true, | ||||
| 				Profile:        ProfileLegacy, | ||||
| 				TargetNames:    []string{"node/mynode"}, | ||||
| 				TTY:            true, | ||||
| 				Args:               []string{}, | ||||
| 				Attach:             true, | ||||
| 				Image:              "busybox", | ||||
| 				Interactive:        true, | ||||
| 				KeepInitContainers: true, | ||||
| 				Namespace:          "test", | ||||
| 				ShareProcesses:     true, | ||||
| 				Profile:            ProfileLegacy, | ||||
| 				TargetNames:        []string{"node/mynode"}, | ||||
| 				TTY:                true, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
|   | ||||
| @@ -64,54 +64,91 @@ type ProfileApplier interface { | ||||
| } | ||||
|  | ||||
| // NewProfileApplier returns a new Options for the given profile name. | ||||
| func NewProfileApplier(profile string) (ProfileApplier, error) { | ||||
| func NewProfileApplier(profile string, kflags KeepFlags) (ProfileApplier, error) { | ||||
| 	switch profile { | ||||
| 	case ProfileLegacy: | ||||
| 		return &legacyProfile{}, nil | ||||
| 		return &legacyProfile{kflags}, nil | ||||
| 	case ProfileGeneral: | ||||
| 		return &generalProfile{}, nil | ||||
| 		return &generalProfile{kflags}, nil | ||||
| 	case ProfileBaseline: | ||||
| 		return &baselineProfile{}, nil | ||||
| 		return &baselineProfile{kflags}, nil | ||||
| 	case ProfileRestricted: | ||||
| 		return &restrictedProfile{}, nil | ||||
| 		return &restrictedProfile{kflags}, nil | ||||
| 	case ProfileNetadmin: | ||||
| 		return &netadminProfile{}, nil | ||||
| 		return &netadminProfile{kflags}, nil | ||||
| 	case ProfileSysadmin: | ||||
| 		return &sysadminProfile{}, nil | ||||
| 		return &sysadminProfile{kflags}, nil | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("unknown profile: %s", profile) | ||||
| } | ||||
|  | ||||
| type legacyProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| type generalProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| type baselineProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| type restrictedProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| type netadminProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| type sysadminProfile struct { | ||||
| 	KeepFlags | ||||
| } | ||||
|  | ||||
| func (p *legacyProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	switch target.(type) { | ||||
| 	case *corev1.Pod: | ||||
| 		// do nothing to the copied pod | ||||
| 		return nil | ||||
| 	case *corev1.Node: | ||||
| 		mountRootPartition(pod, containerName) | ||||
| 		useHostNamespaces(pod) | ||||
| 		return nil | ||||
| 	default: | ||||
| 		return fmt.Errorf("the %s profile doesn't support objects of type %T", ProfileLegacy, target) | ||||
| // KeepFlags holds the flag set that determine which fields to keep in the copy pod. | ||||
| type KeepFlags struct { | ||||
| 	Labels         bool | ||||
| 	Annotations    bool | ||||
| 	Liveness       bool | ||||
| 	Readiness      bool | ||||
| 	Startup        bool | ||||
| 	InitContainers bool | ||||
| } | ||||
|  | ||||
| // RemoveLabels removes labels from the pod. | ||||
| func (kflags KeepFlags) RemoveLabels(p *corev1.Pod) { | ||||
| 	if !kflags.Labels { | ||||
| 		p.Labels = nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // RemoveAnnotations remove annotations from the pod. | ||||
| func (kflags KeepFlags) RemoveAnnotations(p *corev1.Pod) { | ||||
| 	if !kflags.Annotations { | ||||
| 		p.Annotations = nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // RemoveProbes remove probes from all containers of the pod. | ||||
| func (kflags KeepFlags) RemoveProbes(p *corev1.Pod) { | ||||
| 	for i := range p.Spec.Containers { | ||||
| 		if !kflags.Liveness { | ||||
| 			p.Spec.Containers[i].LivenessProbe = nil | ||||
| 		} | ||||
| 		if !kflags.Readiness { | ||||
| 			p.Spec.Containers[i].ReadinessProbe = nil | ||||
| 		} | ||||
| 		if !kflags.Startup { | ||||
| 			p.Spec.Containers[i].StartupProbe = nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // RemoveInitContainers remove initContainers from the pod. | ||||
| func (kflags KeepFlags) RemoveInitContainers(p *corev1.Pod) { | ||||
| 	if !kflags.InitContainers { | ||||
| 		p.Spec.InitContainers = nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -130,10 +167,32 @@ func getDebugStyle(pod *corev1.Pod, target runtime.Object) (debugStyle, error) { | ||||
| 	return unsupported, fmt.Errorf("objects of type %T are not supported", target) | ||||
| } | ||||
|  | ||||
| func (p *legacyProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("legacy profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	switch style { | ||||
| 	case node: | ||||
| 		mountRootPartition(pod, containerName) | ||||
| 		useHostNamespaces(pod) | ||||
|  | ||||
| 	case podCopy: | ||||
| 		p.Labels = false | ||||
| 		p.RemoveLabels(pod) | ||||
|  | ||||
| 	case ephemeral: | ||||
| 		// no additional modifications needed | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *generalProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("general profile: %s", err) | ||||
| 		return fmt.Errorf("general profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	switch style { | ||||
| @@ -143,7 +202,10 @@ func (p *generalProfile) Apply(pod *corev1.Pod, containerName string, target run | ||||
| 		useHostNamespaces(pod) | ||||
|  | ||||
| 	case podCopy: | ||||
| 		removeLabelsAndProbes(pod) | ||||
| 		p.RemoveLabels(pod) | ||||
| 		p.RemoveAnnotations(pod) | ||||
| 		p.RemoveProbes(pod) | ||||
| 		p.RemoveInitContainers(pod) | ||||
| 		allowProcessTracing(pod, containerName) | ||||
| 		shareProcessNamespace(pod) | ||||
|  | ||||
| @@ -157,14 +219,17 @@ func (p *generalProfile) Apply(pod *corev1.Pod, containerName string, target run | ||||
| func (p *baselineProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("baseline profile: %s", err) | ||||
| 		return fmt.Errorf("baseline profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	clearSecurityContext(pod, containerName) | ||||
|  | ||||
| 	switch style { | ||||
| 	case podCopy: | ||||
| 		removeLabelsAndProbes(pod) | ||||
| 		p.RemoveLabels(pod) | ||||
| 		p.RemoveAnnotations(pod) | ||||
| 		p.RemoveProbes(pod) | ||||
| 		p.RemoveInitContainers(pod) | ||||
| 		shareProcessNamespace(pod) | ||||
|  | ||||
| 	case ephemeral, node: | ||||
| @@ -177,7 +242,7 @@ func (p *baselineProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||
| func (p *restrictedProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("restricted profile: %s", err) | ||||
| 		return fmt.Errorf("restricted profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	clearSecurityContext(pod, containerName) | ||||
| @@ -188,6 +253,10 @@ func (p *restrictedProfile) Apply(pod *corev1.Pod, containerName string, target | ||||
|  | ||||
| 	switch style { | ||||
| 	case podCopy: | ||||
| 		p.RemoveLabels(pod) | ||||
| 		p.RemoveAnnotations(pod) | ||||
| 		p.RemoveProbes(pod) | ||||
| 		p.RemoveInitContainers(pod) | ||||
| 		shareProcessNamespace(pod) | ||||
|  | ||||
| 	case ephemeral, node: | ||||
| @@ -200,7 +269,7 @@ func (p *restrictedProfile) Apply(pod *corev1.Pod, containerName string, target | ||||
| func (p *netadminProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("netadmin profile: %s", err) | ||||
| 		return fmt.Errorf("netadmin profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	allowNetadminCapability(pod, containerName) | ||||
| @@ -210,6 +279,10 @@ func (p *netadminProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||
| 		useHostNamespaces(pod) | ||||
|  | ||||
| 	case podCopy: | ||||
| 		p.RemoveLabels(pod) | ||||
| 		p.RemoveAnnotations(pod) | ||||
| 		p.RemoveProbes(pod) | ||||
| 		p.RemoveInitContainers(pod) | ||||
| 		shareProcessNamespace(pod) | ||||
|  | ||||
| 	case ephemeral: | ||||
| @@ -222,7 +295,7 @@ func (p *netadminProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||
| func (p *sysadminProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error { | ||||
| 	style, err := getDebugStyle(pod, target) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("sysadmin profile: %s", err) | ||||
| 		return fmt.Errorf("sysadmin profile: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	setPrivileged(pod, containerName) | ||||
| @@ -234,7 +307,12 @@ func (p *sysadminProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||
|  | ||||
| 	case podCopy: | ||||
| 		// to mimic general, default and baseline | ||||
| 		p.RemoveLabels(pod) | ||||
| 		p.RemoveAnnotations(pod) | ||||
| 		p.RemoveProbes(pod) | ||||
| 		p.RemoveInitContainers(pod) | ||||
| 		shareProcessNamespace(pod) | ||||
|  | ||||
| 	case ephemeral: | ||||
| 		// no additional modifications needed | ||||
| 	} | ||||
| @@ -242,17 +320,6 @@ func (p *sysadminProfile) Apply(pod *corev1.Pod, containerName string, target ru | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // removeLabelsAndProbes removes labels from the pod and remove probes | ||||
| // from all containers of the pod. | ||||
| func removeLabelsAndProbes(p *corev1.Pod) { | ||||
| 	p.Labels = nil | ||||
| 	for i := range p.Spec.Containers { | ||||
| 		p.Spec.Containers[i].LivenessProbe = nil | ||||
| 		p.Spec.Containers[i].ReadinessProbe = nil | ||||
| 		p.Spec.Containers[i].StartupProbe = nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // mountRootPartition mounts the host's root path at "/host" in the container. | ||||
| func mountRootPartition(p *corev1.Pod, containerName string) { | ||||
| 	const volumeName = "host-root" | ||||
|   | ||||
| @@ -34,6 +34,203 @@ var testNode = &corev1.Node{ | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func TestLegacyProfile(t *testing.T) { | ||||
| 	pod := &corev1.Pod{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: "pod"}, | ||||
| 		Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ | ||||
| 			{ | ||||
| 				EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||||
| 					Name: "dbg", Image: "dbgimage", | ||||
| 				}, | ||||
| 			}, | ||||
| 		}}, | ||||
| 	} | ||||
|  | ||||
| 	tests := map[string]struct { | ||||
| 		pod           *corev1.Pod | ||||
| 		containerName string | ||||
| 		target        runtime.Object | ||||
| 		expectPod     *corev1.Pod | ||||
| 		expectErr     bool | ||||
| 	}{ | ||||
| 		"bad inputs results in error": { | ||||
| 			pod:           nil, | ||||
| 			containerName: "dbg", | ||||
| 			target:        runtime.Object(nil), | ||||
| 			expectErr:     true, | ||||
| 		}, | ||||
| 		"debug by ephemeral container": { | ||||
| 			pod:           pod, | ||||
| 			containerName: "dbg", | ||||
| 			target:        pod, | ||||
| 			expectPod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "pod"}, | ||||
| 				Spec: corev1.PodSpec{EphemeralContainers: []corev1.EphemeralContainer{ | ||||
| 					{ | ||||
| 						EphemeralContainerCommon: corev1.EphemeralContainerCommon{Name: "dbg", Image: "dbgimage"}, | ||||
| 					}, | ||||
| 				}}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"debug by pod copy": { | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| 							SecurityContext: &corev1.SecurityContext{ | ||||
| 								Capabilities: &corev1.Capabilities{ | ||||
| 									Add: []corev1.Capability{"NET_ADMIN"}, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectPod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| 							SecurityContext: &corev1.SecurityContext{ | ||||
| 								Capabilities: &corev1.Capabilities{ | ||||
| 									Add: []corev1.Capability{"NET_ADMIN"}, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		"debug by node": { | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "pod"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| 							SecurityContext: &corev1.SecurityContext{ | ||||
| 								Capabilities: &corev1.Capabilities{ | ||||
| 									Add: []corev1.Capability{"NET_ADMIN"}, | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target:        testNode, | ||||
| 			expectPod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "pod"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					HostNetwork: true, | ||||
| 					HostPID:     true, | ||||
| 					HostIPC:     true, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| 							SecurityContext: &corev1.SecurityContext{ | ||||
| 								Capabilities: &corev1.Capabilities{ | ||||
| 									Add: []corev1.Capability{"NET_ADMIN"}, | ||||
| 								}, | ||||
| 							}, | ||||
| 							VolumeMounts: []corev1.VolumeMount{ | ||||
| 								{ | ||||
| 									MountPath: "/host", | ||||
| 									Name:      "host-root", | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 					Volumes: []corev1.Volume{ | ||||
| 						{ | ||||
| 							Name: "host-root", | ||||
| 							VolumeSource: corev1.VolumeSource{ | ||||
| 								HostPath: &corev1.HostPathVolumeSource{Path: "/"}, | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			applier := &legacyProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err != nil) != test.expectErr { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) | ||||
| 			} | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 			if diff := cmp.Diff(test.expectPod, test.pod); diff != "" { | ||||
| 				t.Error("unexpected diff in generated object: (-want +got):\n", diff) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestGeneralProfile(t *testing.T) { | ||||
| 	pod := &corev1.Pod{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{Name: "pod"}, | ||||
| @@ -81,10 +278,25 @@ func TestGeneralProfile(t *testing.T) { | ||||
| 		}, | ||||
| 		"debug by pod copy": { | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| @@ -99,16 +311,32 @@ func TestGeneralProfile(t *testing.T) { | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectPod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| @@ -169,7 +397,8 @@ func TestGeneralProfile(t *testing.T) { | ||||
|  | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			err := (&generalProfile{}).Apply(test.pod, test.containerName, test.target) | ||||
| 			applier := &generalProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err != nil) != test.expectErr { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) | ||||
| 			} | ||||
| @@ -230,20 +459,50 @@ func TestBaselineProfile(t *testing.T) { | ||||
| 		}, | ||||
| 		"debug by pod copy": { | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{Name: "dbg", Image: "dbgimage"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| @@ -251,6 +510,7 @@ func TestBaselineProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					ShareProcessNamespace: pointer.Bool(true), | ||||
| 					InitContainers:        []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| @@ -288,7 +548,8 @@ func TestBaselineProfile(t *testing.T) { | ||||
|  | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			err := (&baselineProfile{}).Apply(test.pod, test.containerName, test.target) | ||||
| 			applier := &baselineProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err != nil) != test.expectErr { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) | ||||
| 			} | ||||
| @@ -357,20 +618,50 @@ func TestRestrictedProfile(t *testing.T) { | ||||
| 		}, | ||||
| 		"debug by pod copy": { | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{Name: "dbg", Image: "dbgimage"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| @@ -378,6 +669,7 @@ func TestRestrictedProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					ShareProcessNamespace: pointer.Bool(true), | ||||
| 					InitContainers:        []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| @@ -441,7 +733,8 @@ func TestRestrictedProfile(t *testing.T) { | ||||
|  | ||||
| 	for name, test := range tests { | ||||
| 		t.Run(name, func(t *testing.T) { | ||||
| 			err := (&restrictedProfile{}).Apply(test.pod, test.containerName, test.target) | ||||
| 			applier := &restrictedProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err != nil) != test.expectErr { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, (err != nil)) | ||||
| 			} | ||||
| @@ -506,20 +799,50 @@ func TestNetAdminProfile(t *testing.T) { | ||||
| 		{ | ||||
| 			name: "debug by pod copy", | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{Name: "dbg", Image: "dbgimage"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| @@ -527,6 +850,7 @@ func TestNetAdminProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					ShareProcessNamespace: pointer.Bool(true), | ||||
| 					InitContainers:        []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| @@ -548,7 +872,13 @@ func TestNetAdminProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| @@ -566,7 +896,13 @@ func TestNetAdminProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| @@ -665,7 +1001,8 @@ func TestNetAdminProfile(t *testing.T) { | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			err := (&netadminProfile{}).Apply(test.pod, test.containerName, test.target) | ||||
| 			applier := &netadminProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err == nil) != (test.expectErr == nil) || (err != nil && test.expectErr != nil && err.Error() != test.expectErr.Error()) { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, err) | ||||
| 			} | ||||
| @@ -728,26 +1065,57 @@ func TestSysAdminProfile(t *testing.T) { | ||||
| 		{ | ||||
| 			name: "debug by pod copy", | ||||
| 			pod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{Name: "dbg", Image: "dbgimage"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			containerName: "dbg", | ||||
| 			target: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				ObjectMeta: metav1.ObjectMeta{ | ||||
| 					Name: "podcopy", | ||||
| 					Labels: map[string]string{ | ||||
| 						"app": "podcopy", | ||||
| 					}, | ||||
| 					Annotations: map[string]string{ | ||||
| 						"test": "test", | ||||
| 					}, | ||||
| 				}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectPod: &corev1.Pod{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					InitContainers: []corev1.Container{{Name: "init-container"}}, | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| @@ -768,7 +1136,13 @@ func TestSysAdminProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name:  "dbg", | ||||
| 							Image: "dbgimage", | ||||
| @@ -786,7 +1160,13 @@ func TestSysAdminProfile(t *testing.T) { | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "podcopy"}, | ||||
| 				Spec: corev1.PodSpec{ | ||||
| 					Containers: []corev1.Container{ | ||||
| 						{Name: "app", Image: "appimage"}, | ||||
| 						{ | ||||
| 							Name:           "app", | ||||
| 							Image:          "appimage", | ||||
| 							LivenessProbe:  &corev1.Probe{}, | ||||
| 							ReadinessProbe: &corev1.Probe{}, | ||||
| 							StartupProbe:   &corev1.Probe{}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| @@ -899,7 +1279,8 @@ func TestSysAdminProfile(t *testing.T) { | ||||
|  | ||||
| 	for _, test := range tests { | ||||
| 		t.Run(test.name, func(t *testing.T) { | ||||
| 			err := (&sysadminProfile{}).Apply(test.pod, test.containerName, test.target) | ||||
| 			applier := &sysadminProfile{KeepFlags{InitContainers: true}} | ||||
| 			err := applier.Apply(test.pod, test.containerName, test.target) | ||||
| 			if (err == nil) != (test.expectErr == nil) || (err != nil && test.expectErr != nil && err.Error() != test.expectErr.Error()) { | ||||
| 				t.Fatalf("expect error: %v, got error: %v", test.expectErr, err) | ||||
| 			} | ||||
|   | ||||
| @@ -26,7 +26,6 @@ run_kubectl_debug_pod_tests() { | ||||
|   kube::log::status "Testing kubectl debug (pod tests)" | ||||
|  | ||||
|   ### Pod Troubleshooting by ephemeral containers | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" is created | ||||
|   kubectl run target "--image=${IMAGE_NGINX:?}" "${kube_flags[@]:?}" | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
| @@ -38,7 +37,6 @@ run_kubectl_debug_pod_tests() { | ||||
|   kubectl delete pod target "${kube_flags[@]:?}" | ||||
|  | ||||
|   ### Pod Troubleshooting by Copy | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" is created | ||||
|   kubectl run target "--image=${IMAGE_NGINX:?}" "${kube_flags[@]:?}" | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
| @@ -51,6 +49,26 @@ run_kubectl_debug_pod_tests() { | ||||
|   # Clean up | ||||
|   kubectl delete pod target target-copy "${kube_flags[@]:?}" | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" with labels, annotations, probes and initContainers is created | ||||
|   kubectl create -f hack/testdata/pod-with-metadata-and-probes.yaml | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
|   # Command: create a copy of target with a new debug container with --keep-* flags | ||||
|   # --keep-* flags intentionally don't work with legacyProfile(Only labels are removed) | ||||
|   kubectl debug target -it --copy-to=target-copy --image=busybox --container=debug-container --keep-labels=true --keep-annotations=true --keep-liveness=true --keep-readiness=true --keep-startup=true --keep-init-containers=false --attach=false "${kube_flags[@]:?}" | ||||
|   # Post-Conditions | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:target-copy:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.labels}}' '<no value>' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.annotations}}' 'map\[test:test\]' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.name}}:{{end}}' 'target:debug-container:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.image}}:{{end}}' "${IMAGE_NGINX:?}:busybox:" | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "livenessProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "readinessProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "startupProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.initContainers}}{{.name}}:{{end}}' 'init:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.initContainers}}{{.image}}:{{end}}' "busybox:" | ||||
|   # Clean up | ||||
|   kubectl delete pod target target-copy "${kube_flags[@]:?}" | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" is created | ||||
|   kubectl run target "--image=${IMAGE_NGINX:?}" "${kube_flags[@]:?}" | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
| @@ -133,20 +151,44 @@ run_kubectl_debug_general_tests() { | ||||
|   kube::log::status "Testing kubectl debug profile general" | ||||
|  | ||||
|   ### Debug by pod copy | ||||
|   ### probes are removed, sets SYS_PTRACE in debugging container, sets shareProcessNamespace | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" is created | ||||
|   kubectl run target "--image=${IMAGE_NGINX:?}" "${kube_flags[@]:?}" | ||||
|   # Pre-Condition: Pod "nginx" with labels, annotations, probes and initContainers is created | ||||
|   kubectl create -f hack/testdata/pod-with-metadata-and-probes.yaml | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
|   # Command: create a copy of target with a new debug container | ||||
|   # labels, annotations, probes are removed and initContainers are kept, sets SYS_PTRACE in debugging container, sets shareProcessNamespace | ||||
|   kubectl debug --profile general target -it --copy-to=target-copy --image=busybox --container=debug-container --attach=false "${kube_flags[@]:?}" | ||||
|   # Post-Conditions | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:target-copy:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.labels}}' '<no value>' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.annotations}}' '<no value>' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.name}}:{{end}}' 'target:debug-container:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.image}}:{{end}}' "${IMAGE_NGINX:?}:busybox:" | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "livenessProbe")}}:{{end}}{{end}}' '' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "readinessProbe")}}:{{end}}{{end}}' '' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "startupProbe")}}:{{end}}{{end}}' '' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.initContainers}}{{.name}}:{{end}}' 'init:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.initContainers}}{{.image}}:{{end}}' "busybox:" | ||||
|   kube::test::get_object_assert pod/target-copy '{{(index (index .spec.containers 1).securityContext.capabilities.add 0)}}' 'SYS_PTRACE' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.spec.shareProcessNamespace}}' 'true' | ||||
|   # Clean up | ||||
|   kubectl delete pod target target-copy "${kube_flags[@]:?}" | ||||
|  | ||||
|   # Pre-Condition: Pod "nginx" with labels, annotations, probes and initContainers is created | ||||
|   kubectl create -f hack/testdata/pod-with-metadata-and-probes.yaml | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:' | ||||
|   # Command: create a copy of target with a new debug container with --keep-* flags | ||||
|   # labels, annotations, probes are kept and initContainers are removed, sets SYS_PTRACE in debugging container, sets shareProcessNamespace | ||||
|   kubectl debug --profile general target -it --copy-to=target-copy --image=busybox --container=debug-container --keep-labels=true --keep-annotations=true --keep-liveness=true --keep-readiness=true --keep-startup=true --keep-init-containers=false --attach=false "${kube_flags[@]:?}" | ||||
|   # Post-Conditions | ||||
|   kube::test::get_object_assert pod "{{range.items}}{{${id_field:?}}}:{{end}}" 'target:target-copy:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.labels}}' 'map\[run:target\]' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.metadata.annotations}}' 'map\[test:test\]' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.name}}:{{end}}' 'target:debug-container:' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{.image}}:{{end}}' "${IMAGE_NGINX:?}:busybox:" | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "livenessProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "readinessProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{range.spec.containers}}{{if (index . "startupProbe")}}:{{end}}{{end}}' ':' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.spec.initContainers}}' '<no value>' | ||||
|   kube::test::get_object_assert pod/target-copy '{{(index (index .spec.containers 1).securityContext.capabilities.add 0)}}' 'SYS_PTRACE' | ||||
|   kube::test::get_object_assert pod/target-copy '{{.spec.shareProcessNamespace}}' 'true' | ||||
|   # Clean up | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 mochizuki875
					mochizuki875