New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement kubectl debug profiles: general, baseline, and restricted #114280
Changes from all commits
9b758da
21f304e
85da9f2
249bfac
c3e82ba
59bc349
5c767f6
5c41ee2
b3d2138
194c8dd
1db7eb6
8ae1fb0
9e9c210
385afff
0f35faa
507f9e7
34ec729
777b04a
5ebd435
61b0b0c
70bd5a4
0edd708
00be236
29ada24
1dbd839
adb553b
93050e8
3b5527d
c1e70d6
457117b
c834ada
914098f
4472c8f
5e61f87
443d72d
98878e9
5db0224
88d19f4
d917ea7
9980209
1624f5f
98f8ff5
7f7ef70
7ccbb6c
f337511
f604758
d3583dc
d760b0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -121,6 +121,7 @@ type DebugOptions struct { | |
TargetContainer string | ||
TTY bool | ||
Profile string | ||
Applier ProfileApplier | ||
|
||
attachChanged bool | ||
shareProcessedChanged bool | ||
|
@@ -129,8 +130,6 @@ type DebugOptions struct { | |
|
||
genericclioptions.IOStreams | ||
WarningPrinter *printers.WarningPrinter | ||
|
||
applier ProfileApplier | ||
} | ||
|
||
// NewDebugOptions returns a DebugOptions initialized with default values. | ||
|
@@ -180,7 +179,7 @@ func addDebugFlags(cmd *cobra.Command, opt *DebugOptions) { | |
cmd.Flags().BoolVar(&opt.ShareProcesses, "share-processes", opt.ShareProcesses, i18n.T("When used with '--copy-to', enable process namespace sharing in the copy.")) | ||
cmd.Flags().StringVar(&opt.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name.")) | ||
cmd.Flags().BoolVarP(&opt.TTY, "tty", "t", opt.TTY, i18n.T("Allocate a TTY for the debugging container.")) | ||
cmd.Flags().StringVar(&opt.Profile, "profile", ProfileLegacy, i18n.T("Debugging profile.")) | ||
cmd.Flags().StringVar(&opt.Profile, "profile", ProfileLegacy, i18n.T(`Debugging profile. Options are "legacy", "general", "baseline", or "restricted".`)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hard coding the list of profiles seems like it could be hard to keep in sync, both with new profiles and in translations. Should we instead have a flag that prints out the list of profiles and exits? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would be useful, but given that the KEP only specified a only handful of profiles - we should be okay? In any case, I think we should defer adding a new flag which prints out the list of profiles to a future MR if possible. For now, I've added a code comment to remind folks in 914098f There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we'll want something better, but maybe it'll come in the form of extensible profiles. I'm fine with hardcoding this to make progress. |
||
} | ||
|
||
// Complete finishes run-time initialization of debug.DebugOptions. | ||
|
@@ -226,9 +225,13 @@ func (o *DebugOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st | |
if o.WarningPrinter == nil { | ||
o.WarningPrinter = printers.NewWarningPrinter(o.ErrOut, printers.WarningPrinterOptions{Color: term.AllowsColorOutput(o.ErrOut)}) | ||
} | ||
o.applier, err = NewProfileApplier(o.Profile) | ||
if err != nil { | ||
return err | ||
|
||
if o.Applier == nil { | ||
applier, err := NewProfileApplier(o.Profile) | ||
if err != nil { | ||
return err | ||
} | ||
o.Applier = applier | ||
} | ||
|
||
return nil | ||
|
@@ -536,10 +539,12 @@ func (o *DebugOptions) generateDebugContainer(pod *corev1.Pod) (*corev1.Pod, *co | |
|
||
copied := pod.DeepCopy() | ||
copied.Spec.EphemeralContainers = append(copied.Spec.EphemeralContainers, *ec) | ||
if err := o.applier.Apply(copied, name, copied); err != nil { | ||
if err := o.Applier.Apply(copied, name, copied); err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
ec = &copied.Spec.EphemeralContainers[len(copied.Spec.EphemeralContainers)-1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the reason of this change with respect to the new debug profiles?. If this is not related to the new profiles, it would be better to open it separate PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this came from knight42 via commit bdb860490bfdf3db225020b2bc77168527394783. It ensures that the returned value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this change related to new debug profiles or is it fixing something?. If it fixes a bug, it is better to fix that independent from this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is related to new debug profiles, because the only pre-existing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the returned ec references is only used for two reasons:
Maybe we should just get rid of returning a pointer to the ephemeral container and just return the name instead? The section might read a little more clearly with an immediate pod copy, something like:
Both of these suggestions are optional, feel free to disregard or fix in a follow-up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, I've made #115285 to track that. I'd be happy to circle back around on this in a future MR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 thanks |
||
|
||
return copied, ec, nil | ||
} | ||
|
||
|
@@ -593,7 +598,7 @@ func (o *DebugOptions) generateNodeDebugPod(node *corev1.Node) (*corev1.Pod, err | |
p.Spec.Containers[0].Command = o.Args | ||
} | ||
|
||
if err := o.applier.Apply(p, cn, node); err != nil { | ||
if err := o.Applier.Apply(p, cn, node); err != nil { | ||
return nil, err | ||
} | ||
|
||
|
@@ -614,7 +619,7 @@ func (o *DebugOptions) generatePodCopyWithDebugContainer(pod *corev1.Pod) (*core | |
copied.Spec.EphemeralContainers = nil | ||
// change ShareProcessNamespace configuration only when commanded explicitly | ||
if o.shareProcessedChanged { | ||
copied.Spec.ShareProcessNamespace = pointer.BoolPtr(o.ShareProcesses) | ||
copied.Spec.ShareProcessNamespace = pointer.Bool(o.ShareProcesses) | ||
} | ||
if !o.SameNode { | ||
copied.Spec.NodeName = "" | ||
|
@@ -647,13 +652,11 @@ func (o *DebugOptions) generatePodCopyWithDebugContainer(pod *corev1.Pod) (*core | |
if len(name) == 0 { | ||
name = o.computeDebugContainerName(copied) | ||
} | ||
c = &corev1.Container{ | ||
copied.Spec.Containers = append(copied.Spec.Containers, corev1.Container{ | ||
Name: name, | ||
TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||
} | ||
defer func() { | ||
copied.Spec.Containers = append(copied.Spec.Containers, *c) | ||
}() | ||
}) | ||
c = &copied.Spec.Containers[len(copied.Spec.Containers)-1] | ||
} | ||
|
||
if len(o.Args) > 0 { | ||
|
@@ -676,7 +679,7 @@ func (o *DebugOptions) generatePodCopyWithDebugContainer(pod *corev1.Pod) (*core | |
c.Stdin = o.Interactive | ||
c.TTY = o.TTY | ||
|
||
err := o.applier.Apply(copied, c.Name, pod) | ||
err := o.Applier.Apply(copied, c.Name, pod) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
Container: "debugger", | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
|
@@ -72,6 +73,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
TargetContainer: "myapp", | ||
Profile: ProfileLegacy, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
|
@@ -90,6 +92,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
Container: "debugger", | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
|
@@ -109,6 +112,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
Args: []string{"echo", "one", "two", "three"}, | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
|
@@ -125,6 +129,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
|
@@ -140,6 +145,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
pod: &corev1.Pod{ | ||
Spec: corev1.PodSpec{ | ||
|
@@ -164,6 +170,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
pod: &corev1.Pod{ | ||
Spec: corev1.PodSpec{ | ||
|
@@ -196,6 +203,7 @@ func TestGenerateDebugContainer(t *testing.T) { | |
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileLegacy, | ||
}, | ||
pod: &corev1.Pod{ | ||
Spec: corev1.PodSpec{ | ||
|
@@ -227,6 +235,65 @@ func TestGenerateDebugContainer(t *testing.T) { | |
}, | ||
}, | ||
}, | ||
{ | ||
name: "general profile", | ||
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileGeneral, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
Name: "debugger-1", | ||
Image: "busybox", | ||
ImagePullPolicy: corev1.PullIfNotPresent, | ||
TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||
SecurityContext: &corev1.SecurityContext{ | ||
Capabilities: &corev1.Capabilities{ | ||
Add: []corev1.Capability{"SYS_PTRACE"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "baseline profile", | ||
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileBaseline, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
Name: "debugger-1", | ||
Image: "busybox", | ||
ImagePullPolicy: corev1.PullIfNotPresent, | ||
TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "restricted profile", | ||
opts: &DebugOptions{ | ||
Image: "busybox", | ||
PullPolicy: corev1.PullIfNotPresent, | ||
Profile: ProfileRestricted, | ||
}, | ||
expected: &corev1.EphemeralContainer{ | ||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{ | ||
Name: "debugger-1", | ||
Image: "busybox", | ||
ImagePullPolicy: corev1.PullIfNotPresent, | ||
TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||
SecurityContext: &corev1.SecurityContext{ | ||
RunAsNonRoot: pointer.Bool(true), | ||
Capabilities: &corev1.Capabilities{ | ||
Drop: []corev1.Capability{"ALL"}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} { | ||
t.Run(tc.name, func(t *testing.T) { | ||
tc.opts.IOStreams = genericclioptions.NewTestIOStreamsDiscard() | ||
|
@@ -236,11 +303,11 @@ func TestGenerateDebugContainer(t *testing.T) { | |
tc.pod = &corev1.Pod{} | ||
} | ||
|
||
applier, err := NewProfileApplier(ProfileLegacy) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I suggested above, we can preserve this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, done in 8176751066f175f7bf53cbaf88a8c20ff2adc |
||
applier, err := NewProfileApplier(tc.opts.Profile) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Is there any reason of changing the order with |
||
if err != nil { | ||
t.Fatalf("fail to create %s profile", ProfileLegacy) | ||
t.Fatalf("failed to create profile applier: %s: %v", tc.opts.Profile, err) | ||
} | ||
tc.opts.applier = applier | ||
tc.opts.Applier = applier | ||
|
||
_, debugContainer, err := tc.opts.generateDebugContainer(tc.pod) | ||
if err != nil { | ||
|
@@ -814,7 +881,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { | |
TerminationMessagePolicy: corev1.TerminationMessageReadFile, | ||
}, | ||
}, | ||
ShareProcessNamespace: pointer.BoolPtr(true), | ||
ShareProcessNamespace: pointer.Bool(true), | ||
}, | ||
}, | ||
}, | ||
|
@@ -1017,7 +1084,7 @@ func TestGeneratePodCopyWithDebugContainer(t *testing.T) { | |
} { | ||
t.Run(tc.name, func(t *testing.T) { | ||
var err error | ||
tc.opts.applier, err = NewProfileApplier(ProfileLegacy) | ||
tc.opts.Applier, err = NewProfileApplier(ProfileLegacy) | ||
if err != nil { | ||
t.Fatalf("Fail to create legacy profile: %v", err) | ||
} | ||
|
@@ -1212,7 +1279,7 @@ func TestGenerateNodeDebugPod(t *testing.T) { | |
} { | ||
t.Run(tc.name, func(t *testing.T) { | ||
var err error | ||
tc.opts.applier, err = NewProfileApplier(ProfileLegacy) | ||
tc.opts.Applier, err = NewProfileApplier(ProfileLegacy) | ||
if err != nil { | ||
t.Fatalf("Fail to create legacy profile: %v", err) | ||
} | ||
|
@@ -1236,7 +1303,7 @@ func TestCompleteAndValidate(t *testing.T) { | |
cmpFilter := cmp.FilterPath(func(p cmp.Path) bool { | ||
switch p.String() { | ||
// IOStreams contains unexported fields | ||
case "IOStreams": | ||
case "IOStreams", "Applier": | ||
return true | ||
} | ||
return false | ||
|
@@ -1572,7 +1639,6 @@ func TestCompleteAndValidate(t *testing.T) { | |
t.Run(tc.name, func(t *testing.T) { | ||
opts := NewDebugOptions(ioStreams) | ||
var gotError error | ||
|
||
cmd := &cobra.Command{ | ||
Run: func(cmd *cobra.Command, args []string) { | ||
gotError = opts.Complete(tf, cmd, args) | ||
|
@@ -1599,7 +1665,7 @@ func TestCompleteAndValidate(t *testing.T) { | |
} | ||
|
||
if diff := cmp.Diff(tc.wantOpts, opts, cmpFilter, cmpopts.IgnoreFields(DebugOptions{}, | ||
"attachChanged", "shareProcessedChanged", "podClient", "WarningPrinter", "applier")); diff != "" { | ||
"attachChanged", "shareProcessedChanged", "podClient", "WarningPrinter", "Applier")); diff != "" { | ||
t.Error("CompleteAndValidate unexpected diff in generated object: (-want +got):\n", diff) | ||
} | ||
}) | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you help me catch up on why this is now exported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arda suggested this during review of this MR:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks for catching me up.
I don't think I quite understand what's going to set the
Applier
if notkubectl debug
, but I support the use case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically for the other CLI's like
oc
to usekubectl debug
.oc
has it's own custom profiles and it will setApplier
with custom profiles.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sgtm, happy for
oc
to innovate here (and hopefully push what works back upstream 😁)