// Run executes a validated remote execution against a pod. func (p *AttachOptions) Run() error { if p.Pod == nil { pod, err := p.Client.Pods(p.Namespace).Get(p.PodName) if err != nil { return err } if pod.Status.Phase != api.PodRunning { return fmt.Errorf("pod %s is not running and cannot be attached to; current phase is %s", p.PodName, pod.Status.Phase) } p.Pod = pod // TODO: convert this to a clean "wait" behavior } pod := p.Pod // ensure we can recover the terminal while attached t := term.TTY{Parent: p.InterruptParent} // check for TTY tty := p.TTY containerToAttach := p.GetContainer(pod) if tty && !containerToAttach.TTY { tty = false fmt.Fprintf(p.Err, "Unable to use a TTY - container %s did not allocate one\n", containerToAttach.Name) } if p.Stdin { t.In = p.In if tty && !t.IsTerminal() { tty = false fmt.Fprintln(p.Err, "Unable to use a TTY - input is not a terminal or the right kind of file") } } t.Raw = tty fn := func() error { if tty { fmt.Fprintln(p.Out, "\nHit enter for command prompt") } // TODO: consider abstracting into a client invocation or client helper req := p.Client.RESTClient.Post(). Resource("pods"). Name(pod.Name). Namespace(pod.Namespace). SubResource("attach") req.VersionedParams(&api.PodAttachOptions{ Container: containerToAttach.Name, Stdin: p.In != nil, Stdout: p.Out != nil, Stderr: p.Err != nil, TTY: tty, }, api.ParameterCodec) return p.Attach.Attach("POST", req.URL(), p.Config, p.In, p.Out, p.Err, tty) } if err := t.Safe(fn); err != nil { return err } if p.Stdin && tty && pod.Spec.RestartPolicy == api.RestartPolicyAlways { fmt.Fprintf(p.Out, "Session ended, resume using '%s %s -c %s -i -t' command when the pod is running\n", p.CommandName, pod.Name, containerToAttach.Name) } return nil }