// 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.PodSucceeded || pod.Status.Phase == api.PodFailed { return fmt.Errorf("cannot attach a container in a completed pod; current phase is %s", 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, Out: p.Out, } // check for TTY tty := p.TTY containerToAttach, err := p.containerToAttachTo(pod) if err != nil { return fmt.Errorf("cannot attach to the container: %v", err) } 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.IsTerminalIn() { tty = false fmt.Fprintln(p.Err, "Unable to use a TTY - input is not a terminal or the right kind of file") } } else { p.In = nil } t.Raw = tty // save p.Err so we can print the command prompt message below stderr := p.Err var sizeQueue term.TerminalSizeQueue if tty { if size := t.GetSize(); size != nil { // fake resizing +1 and then back to normal so that attach-detach-reattach will result in the // screen being redrawn sizePlusOne := *size sizePlusOne.Width++ sizePlusOne.Height++ // this call spawns a goroutine to monitor/update the terminal size sizeQueue = t.MonitorSize(&sizePlusOne, size) } // unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is // true p.Err = nil } fn := func() error { if stderr != nil { fmt.Fprintln(stderr, "If you don't see a command prompt, try pressing enter.") } // 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.Stdin, 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, sizeQueue) } 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 }
// 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 }
// Run executes a validated remote execution against a pod. func (p *ExecOptions) Run() error { pod, err := p.Client.Pods(p.Namespace).Get(p.PodName) if err != nil { return err } if pod.Status.Phase == api.PodSucceeded || pod.Status.Phase == api.PodFailed { return fmt.Errorf("cannot exec into a container in a completed pod; current phase is %s", pod.Status.Phase) } containerName := p.ContainerName if len(containerName) == 0 { glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name) containerName = pod.Spec.Containers[0].Name } // ensure we can recover the terminal while attached t := term.TTY{ Parent: p.InterruptParent, Out: p.Out, } // check for TTY tty := p.TTY if p.Stdin { t.In = p.In if tty && !t.IsTerminalIn() { tty = false fmt.Fprintln(p.Err, "Unable to use a TTY - input is not a terminal or the right kind of file") } } else { p.In = nil } t.Raw = tty var sizeQueue term.TerminalSizeQueue if tty { // this call spawns a goroutine to monitor/update the terminal size sizeQueue = t.MonitorSize(t.GetSize()) // unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is // true p.Err = nil } fn := func() error { // TODO: consider abstracting into a client invocation or client helper req := p.Client.RESTClient.Post(). Resource("pods"). Name(pod.Name). Namespace(pod.Namespace). SubResource("exec"). Param("container", containerName) req.VersionedParams(&api.PodExecOptions{ Container: containerName, Command: p.Command, Stdin: p.Stdin, Stdout: p.Out != nil, Stderr: p.Err != nil, TTY: tty, }, api.ParameterCodec) return p.Executor.Execute("POST", req.URL(), p.Config, p.In, p.Out, p.Err, tty, sizeQueue) } if err := t.Safe(fn); err != nil { return err } return nil }