Skip to content

Commit 1caf9a1

Browse files
authored
KEP-4292: Add e2e test for custom profile in kubectl debug (kubernetes#127187)
* Remove KUBECTL_DEBUG_CUSTOM_PROFILE env var * Add e2e test for custom profile in kubectl debug * Keep feature flag until 1.33 * Update comment * Simplify tests by relying on test framework functionality * Rename import alias to better to pass verify-import-alias
1 parent c4a14d7 commit 1caf9a1

File tree

3 files changed

+164
-4
lines changed

3 files changed

+164
-4
lines changed

staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,7 @@ func (o *DebugOptions) AddFlags(cmd *cobra.Command) {
212212
cmd.Flags().StringVar(&o.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name."))
213213
cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, i18n.T("Allocate a TTY for the debugging container."))
214214
cmd.Flags().StringVar(&o.Profile, "profile", ProfileLegacy, i18n.T(`Options are "legacy", "general", "baseline", "netadmin", "restricted" or "sysadmin".`))
215-
if !cmdutil.DebugCustomProfile.IsDisabled() {
216-
cmd.Flags().StringVar(&o.CustomProfileFile, "custom", o.CustomProfileFile, i18n.T("Path to a JSON or YAML file containing a partial container spec to customize built-in debug profiles."))
217-
}
215+
cmd.Flags().StringVar(&o.CustomProfileFile, "custom", o.CustomProfileFile, i18n.T("Path to a JSON or YAML file containing a partial container spec to customize built-in debug profiles."))
218216
}
219217

220218
// Complete finishes run-time initialization of debug.DebugOptions.

staging/src/k8s.io/kubectl/pkg/cmd/util/helpers.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ const (
430430
OpenAPIV3Patch FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
431431
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
432432
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
433-
DebugCustomProfile FeatureGate = "KUBECTL_DEBUG_CUSTOM_PROFILE"
433+
// DebugCustomProfile should be dropped in 1.34
434+
DebugCustomProfile FeatureGate = "KUBECTL_DEBUG_CUSTOM_PROFILE"
434435
)
435436

436437
// IsEnabled returns true iff environment variable is set to true.

test/e2e/kubectl/debug.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// OWNER = sig/cli
18+
19+
package kubectl
20+
21+
import (
22+
"context"
23+
"os"
24+
"path/filepath"
25+
26+
"github.com/onsi/ginkgo/v2"
27+
"github.com/onsi/gomega"
28+
29+
"k8s.io/apimachinery/pkg/labels"
30+
clientset "k8s.io/client-go/kubernetes"
31+
"k8s.io/kubernetes/test/e2e/framework"
32+
e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
33+
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
34+
testutils "k8s.io/kubernetes/test/utils"
35+
imageutils "k8s.io/kubernetes/test/utils/image"
36+
admissionapi "k8s.io/pod-security-admission/api"
37+
)
38+
39+
var _ = SIGDescribe("kubectl debug", func() {
40+
f := framework.NewDefaultFramework("kubectl-debug")
41+
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
42+
43+
var ns string
44+
var c clientset.Interface
45+
ginkgo.BeforeEach(func() {
46+
c = f.ClientSet
47+
ns = f.Namespace.Name
48+
})
49+
50+
ginkgo.Describe("custom profile", func() {
51+
ginkgo.It("should be applied on static profiles on ephemeral container", func(ctx context.Context) {
52+
debugPodName := "e2e-test-debug-custom-profile-pod-ephemeral"
53+
customProfileImage := imageutils.GetE2EImage(imageutils.BusyBox)
54+
ginkgo.By("running the image " + customProfileImage)
55+
e2ekubectl.RunKubectlOrDie(ns,
56+
"run", debugPodName,
57+
"--image="+customProfileImage,
58+
podRunningTimeoutArg,
59+
"--labels=run="+debugPodName,
60+
"--", "sh", "-c",
61+
"sleep 3600")
62+
63+
ginkgo.By("verifying the pod " + debugPodName + " is running")
64+
label := labels.SelectorFromSet(map[string]string{"run": debugPodName})
65+
_, err := e2epod.WaitForPodsWithLabelRunningReady(ctx, c, ns, label, 1, framework.PodStartShortTimeout)
66+
if err != nil {
67+
framework.Failf("Failed getting pod %s: %v", debugPodName, err)
68+
}
69+
70+
tmpDir, err := os.MkdirTemp("", "test-custom-profile-debug")
71+
framework.ExpectNoError(err)
72+
defer os.Remove(tmpDir) //nolint:errcheck
73+
customProfileFile := "custom.yaml"
74+
framework.ExpectNoError(os.WriteFile(filepath.Join(tmpDir, customProfileFile), []byte(`
75+
securityContext:
76+
capabilities:
77+
add:
78+
- NET_ADMIN
79+
env:
80+
- name: REQUIRED_ENV_VAR
81+
value: value2
82+
`), os.FileMode(0755)), "creating a custom.yaml in temp directory")
83+
ginkgo.By("verifying ephemeral container has correct fields")
84+
e2ekubectl.RunKubectlOrDie(ns,
85+
"debug",
86+
debugPodName,
87+
"--image="+imageutils.GetE2EImage(imageutils.Nginx),
88+
"--container=debugger",
89+
"--profile=general", // general profile sets capability to SYS_PTRACE and custom should overwrite it to NET_ADMIN
90+
"--custom="+filepath.Join(tmpDir, customProfileFile),
91+
"--", "sh", "-c",
92+
"sleep 3600")
93+
94+
ginkgo.By("verifying the container debugger is running")
95+
framework.ExpectNoError(e2epod.WaitForContainerRunning(ctx, c, ns, debugPodName, "debugger", framework.PodStartShortTimeout))
96+
output := e2ekubectl.RunKubectlOrDie(ns, "get", "pod", debugPodName, "-o", "jsonpath={.spec.ephemeralContainers[*]}")
97+
ginkgo.By("verifying NET_ADMIN is added")
98+
gomega.Expect(output).To(gomega.ContainSubstring("NET_ADMIN"))
99+
ginkgo.By("verifying SYS_PTRACE is overridden")
100+
gomega.Expect(output).NotTo(gomega.ContainSubstring("SYS_PTRACE"))
101+
ginkgo.By("verifying REQUIRED_ENV_VAR is added")
102+
gomega.Expect(output).To(gomega.ContainSubstring("REQUIRED_ENV_VAR"))
103+
})
104+
105+
ginkgo.It("should be applied on static profiles while copying from pod", func(ctx context.Context) {
106+
debugPodName := "e2e-test-debug-custom-profile-pod"
107+
customProfileImage := imageutils.GetE2EImage(imageutils.BusyBox)
108+
ginkgo.By("running the image " + customProfileImage)
109+
e2ekubectl.RunKubectlOrDie(ns,
110+
"run", debugPodName,
111+
"--image="+customProfileImage,
112+
podRunningTimeoutArg,
113+
"--labels=run="+debugPodName,
114+
"--", "sh", "-c",
115+
"sleep 3600")
116+
117+
ginkgo.By("verifying the pod " + debugPodName + " is running")
118+
label := labels.SelectorFromSet(map[string]string{"run": debugPodName})
119+
err := testutils.WaitForPodsWithLabelRunning(c, ns, label)
120+
if err != nil {
121+
framework.Failf("Failed getting pod %s: %v", debugPodName, err)
122+
}
123+
124+
tmpDir, err := os.MkdirTemp("", "test-custom-profile-debug")
125+
framework.ExpectNoError(err)
126+
defer os.Remove(tmpDir) //nolint:errcheck
127+
customProfileFile := "custom.yaml"
128+
framework.ExpectNoError(os.WriteFile(filepath.Join(tmpDir, customProfileFile), []byte(`
129+
securityContext:
130+
capabilities:
131+
add:
132+
- NET_ADMIN
133+
env:
134+
- name: REQUIRED_ENV_VAR
135+
value: value2
136+
`), os.FileMode(0755)), "creating a custom.yaml in temp directory")
137+
ginkgo.By("verifying copied pod has correct fields")
138+
e2ekubectl.RunKubectlOrDie(ns,
139+
"debug",
140+
debugPodName,
141+
"--image="+imageutils.GetE2EImage(imageutils.Nginx),
142+
"--copy-to=my-debugger",
143+
"--container=debugger",
144+
"--profile=general", // general profile sets capability to SYS_PTRACE and custom should overwrite it to NET_ADMIN
145+
"--custom="+filepath.Join(tmpDir, customProfileFile),
146+
"--", "sh", "-c",
147+
"sleep 3600")
148+
149+
ginkgo.By("verifying the container in pod my-debugger is running")
150+
framework.ExpectNoError(e2epod.WaitForContainerRunning(ctx, c, ns, "my-debugger", "debugger", framework.PodStartShortTimeout))
151+
152+
output := e2ekubectl.RunKubectlOrDie(ns, "get", "pod", "my-debugger", "-o", "jsonpath={.spec.containers[*]}")
153+
ginkgo.By("verifying NET_ADMIN is added")
154+
gomega.Expect(output).To(gomega.ContainSubstring("NET_ADMIN"))
155+
ginkgo.By("verifying SYS_PTRACE is overridden")
156+
gomega.Expect(output).NotTo(gomega.ContainSubstring("SYS_PTRACE"))
157+
ginkgo.By("verifying REQUIRED_ENV_VAR is added")
158+
gomega.Expect(output).To(gomega.ContainSubstring("REQUIRED_ENV_VAR"))
159+
})
160+
})
161+
})

0 commit comments

Comments
 (0)