diff --git a/pkg/cmd/attach/attach.go b/pkg/cmd/attach/attach.go index bacf3f5c..668524ab 100644 --- a/pkg/cmd/attach/attach.go +++ b/pkg/cmd/attach/attach.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "net/url" + "strings" "time" "github.com/spf13/cobra" @@ -286,8 +287,8 @@ func (o *AttachOptions) Run() error { return err } - if !o.Quiet && o.Stdin && t.Raw && o.Pod.Spec.RestartPolicy == corev1.RestartPolicyAlways { - fmt.Fprintf(o.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", o.CommandName, o.Pod.Name, containerToAttach.Name) + if msg := o.reattachMessage(containerToAttach.Name, t.Raw); msg != "" { + fmt.Fprintln(o.Out, msg) } return nil } @@ -317,3 +318,15 @@ func (o *AttachOptions) GetContainerName(pod *corev1.Pod) (string, error) { } return c.Name, nil } + +// reattachMessage returns a message to print after attach has completed, or +// the empty string if no message should be printed. +func (o *AttachOptions) reattachMessage(containerName string, rawTTY bool) string { + if o.Quiet || !o.Stdin || !rawTTY || o.Pod.Spec.RestartPolicy != corev1.RestartPolicyAlways { + return "" + } + if _, path := podcmd.FindContainerByName(o.Pod, containerName); strings.HasPrefix(path, "spec.ephemeralContainers") { + return fmt.Sprintf("Session ended, the ephemeral container will not be restarted but may be reattached using '%s %s -c %s -i -t' if it is still running", o.CommandName, o.Pod.Name, containerName) + } + return fmt.Sprintf("Session ended, resume using '%s %s -c %s -i -t' command when the pod is running", o.CommandName, o.Pod.Name, containerName) +} diff --git a/pkg/cmd/attach/attach_test.go b/pkg/cmd/attach/attach_test.go index c9b809a4..451f6915 100644 --- a/pkg/cmd/attach/attach_test.go +++ b/pkg/cmd/attach/attach_test.go @@ -484,3 +484,76 @@ func setDefaultContainer(pod *corev1.Pod, name string) *corev1.Pod { pod.Annotations[podcmd.DefaultContainerAnnotationName] = name return pod } + +func TestReattachMessage(t *testing.T) { + tests := []struct { + name string + pod *corev1.Pod + rawTTY, stdin bool + container string + expected string + }{ + { + name: "normal interactive session", + pod: attachPod(), + container: "bar", + rawTTY: true, + stdin: true, + expected: "Session ended, resume using", + }, + { + name: "no stdin", + pod: attachPod(), + container: "bar", + rawTTY: true, + stdin: false, + expected: "", + }, + { + name: "not connected to a real TTY", + pod: attachPod(), + container: "bar", + rawTTY: false, + stdin: true, + expected: "", + }, + { + name: "no restarts", + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test"}, + Spec: corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + Containers: []corev1.Container{{Name: "bar"}}, + }, + Status: corev1.PodStatus{Phase: corev1.PodRunning}, + }, + container: "bar", + rawTTY: true, + stdin: true, + expected: "", + }, + { + name: "ephemeral container", + pod: attachPod(), + container: "debugger", + rawTTY: true, + stdin: true, + expected: "Session ended, the ephemeral container will not be restarted", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + options := &AttachOptions{ + StreamOptions: exec.StreamOptions{ + Stdin: test.stdin, + }, + Pod: test.pod, + } + if msg := options.reattachMessage(test.container, test.rawTTY); test.expected == "" && msg != "" { + t.Errorf("reattachMessage(%v, %v) = %q, want empty string", test.container, test.rawTTY, msg) + } else if !strings.Contains(msg, test.expected) { + t.Errorf("reattachMessage(%v, %v) = %q, want string containing %q", test.container, test.rawTTY, msg, test.expected) + } + }) + } +} diff --git a/pkg/cmd/debug/debug.go b/pkg/cmd/debug/debug.go index f4b4b6b3..ea280407 100644 --- a/pkg/cmd/debug/debug.go +++ b/pkg/cmd/debug/debug.go @@ -364,7 +364,6 @@ func (o *DebugOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error { TTY: o.TTY, Quiet: o.Quiet, }, - // TODO(verb): kubectl prints an incorrect "Session ended" message for debug containers. CommandName: cmd.Parent().CommandPath() + " attach", Attach: &attach.DefaultRemoteAttach{},