Beispiel #1
0
func (o *StreamOptions) setupTTY() term.TTY {
	t := term.TTY{
		Parent: o.InterruptParent,
		Out:    o.Out,
	}

	if !o.Stdin {
		// need to nil out o.In to make sure we don't create a stream for stdin
		o.In = nil
		o.TTY = false
		return t
	}

	t.In = o.In
	if !o.TTY {
		return t
	}

	if o.isTerminalIn == nil {
		o.isTerminalIn = func(tty term.TTY) bool {
			return tty.IsTerminalIn()
		}
	}
	if !o.isTerminalIn(t) {
		o.TTY = false

		if o.Err != nil {
			fmt.Fprintln(o.Err, "Unable to use a TTY - input is not a terminal or the right kind of file")
		}

		return t
	}

	// if we get to here, the user wants to attach stdin, wants a TTY, and o.In is a terminal, so we
	// can safely set t.Raw to true
	t.Raw = true

	if o.overrideStreams == nil {
		// use dockerterm.StdStreams() to get the right I/O handles on Windows
		o.overrideStreams = dockerterm.StdStreams
	}
	stdin, stdout, _ := o.overrideStreams()
	o.In = stdin
	t.In = stdin
	if o.Out != nil {
		o.Out = stdout
		t.Out = stdout
	}

	return t
}
Beispiel #2
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.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
}
Beispiel #3
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
}
Beispiel #4
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
}