Example #1
0
// 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
}
Example #2
0
// 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
}