Skip to content

Commit

Permalink
Add backwards compatibility for kubectl debug
Browse files Browse the repository at this point in the history
The ephemeral containers API changed in 1.22. As a result, kubectl
debug (currently) cannot create ephemeral containers in clusters prior
to 1.22.

This change causes kubectl to retry the request using the old API when
it receives a specific error message from the server.
  • Loading branch information
verb committed Jul 5, 2021
1 parent a0f9c8c commit 06124c1
Showing 1 changed file with 43 additions and 1 deletion.
44 changes: 43 additions & 1 deletion staging/src/k8s.io/kubectl/pkg/cmd/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ type DebugOptions struct {
attachChanged bool
shareProcessedChanged bool

podClient corev1client.PodsGetter
podClient corev1client.CoreV1Interface

genericclioptions.IOStreams
}
Expand Down Expand Up @@ -413,12 +413,54 @@ func (o *DebugOptions) debugByEphemeralContainer(ctx context.Context, pod *corev
if serr, ok := err.(*errors.StatusError); ok && serr.Status().Reason == metav1.StatusReasonNotFound && serr.ErrStatus.Details.Name == "" {
return nil, "", fmt.Errorf("ephemeral containers are disabled for this cluster (error from server: %q).", err)
}

// The Kind used for the /ephemeralcontainers subresource changed in 1.22. When presented with an unexpected
// Kind the api server will respond with a not-registered error. When this happens we can optimistically try
// using the old API.
if runtime.IsNotRegisteredError(err) {
klog.V(1).Infof("Falling back to legacy API because server returned error: %v", err)
return o.debugByEphemeralContainerLegacy(ctx, pod, debugContainer)
}

return nil, "", err
}

return result, debugContainer.Name, nil
}

// debugByEphemeralContainerLegacy adds debugContainer as an ephemeral container using the pre-1.22 /ephemeralcontainers API
// This may be removed when we no longer wish to support releases prior to 1.22.
func (o *DebugOptions) debugByEphemeralContainerLegacy(ctx context.Context, pod *corev1.Pod, debugContainer *corev1.EphemeralContainer) (*corev1.Pod, string, error) {
// We no longer have the v1.EphemeralContainers Kind since it was removed in 1.22, but
// we can present a JSON 6902 patch that the api server will apply.
patch, err := json.Marshal([]map[string]interface{}{{
"op": "add",
"path": "/ephemeralContainers/-",
"value": debugContainer,
}})
if err != nil {
return nil, "", fmt.Errorf("error creating JSON 6902 patch for old /ephemeralcontainers API: %s", err)
}

result := o.podClient.RESTClient().Patch(types.JSONPatchType).
Namespace(pod.Namespace).
Resource("pods").
Name(pod.Name).
SubResource("ephemeralcontainers").
Body(patch).
Do(ctx)
if err := result.Error(); err != nil {
return nil, "", err
}

newPod, err := o.podClient.Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
if err != nil {
return nil, "", err
}

return newPod, debugContainer.Name, nil
}

// debugByCopy runs a copy of the target Pod with a debug container added or an original container modified
func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev1.Pod, string, error) {
copied, dc, err := o.generatePodCopyWithDebugContainer(pod)
Expand Down

0 comments on commit 06124c1

Please sign in to comment.