Example #1
0
func (e *Streamer) setupRequestParameters(obj runtime.Object) error {
	versioned, err := api.Scheme.ConvertToVersion(obj, e.config.Version)
	if err != nil {
		return err
	}
	params, err := queryparams.Convert(versioned)
	if err != nil {
		return err
	}
	for k, v := range params {
		for _, vv := range v {
			e.req.Param(k, vv)
		}
	}
	return nil
}
Example #2
0
// Execute sends a remote command execution request, upgrading the
// connection and creating streams to represent stdin/stdout/stderr. Data is
// copied between these streams and the supplied stdin/stdout/stderr parameters.
func (e *Executor) Execute() error {
	opts := api.PodExecOptions{
		Stdin:   (e.stdin != nil),
		Stdout:  (e.stdout != nil),
		Stderr:  (!e.tty && e.stderr != nil),
		TTY:     e.tty,
		Command: e.command,
	}

	versioned, err := api.Scheme.ConvertToVersion(&opts, e.config.Version)
	if err != nil {
		return err
	}
	params, err := queryparams.Convert(versioned)
	if err != nil {
		return err
	}
	for k, v := range params {
		for _, vv := range v {
			e.req.Param(k, vv)
		}
	}

	if e.upgrader == nil {
		e.upgrader = &defaultUpgrader{}
	}
	conn, err := e.upgrader.upgrade(e.req, e.config)
	if err != nil {
		return err
	}
	defer conn.Close()

	doneChan := make(chan struct{}, 2)
	errorChan := make(chan error)

	cp := func(s string, dst io.Writer, src io.Reader) {
		glog.V(4).Infof("Copying %s", s)
		defer glog.V(4).Infof("Done copying %s", s)
		if _, err := io.Copy(dst, src); err != nil && err != io.EOF {
			glog.Errorf("Error copying %s: %v", s, err)
		}
		if s == api.StreamTypeStdout || s == api.StreamTypeStderr {
			doneChan <- struct{}{}
		}
	}

	headers := http.Header{}
	headers.Set(api.StreamType, api.StreamTypeError)
	errorStream, err := conn.CreateStream(headers)
	if err != nil {
		return err
	}
	go func() {
		message, err := ioutil.ReadAll(errorStream)
		if err != nil && err != io.EOF {
			errorChan <- fmt.Errorf("Error reading from error stream: %s", err)
			return
		}
		if len(message) > 0 {
			errorChan <- fmt.Errorf("Error executing remote command: %s", message)
			return
		}
	}()
	defer errorStream.Reset()

	if opts.Stdin {
		headers.Set(api.StreamType, api.StreamTypeStdin)
		remoteStdin, err := conn.CreateStream(headers)
		if err != nil {
			return err
		}
		defer remoteStdin.Reset()
		// TODO this goroutine will never exit cleanly (the io.Copy never unblocks)
		// because stdin is not closed until the process exits. If we try to call
		// stdin.Close(), it returns no error but doesn't unblock the copy. It will
		// exit when the process exits, instead.
		go cp(api.StreamTypeStdin, remoteStdin, e.stdin)
	}

	waitCount := 0
	completedStreams := 0

	if opts.Stdout {
		waitCount++
		headers.Set(api.StreamType, api.StreamTypeStdout)
		remoteStdout, err := conn.CreateStream(headers)
		if err != nil {
			return err
		}
		defer remoteStdout.Reset()
		go cp(api.StreamTypeStdout, e.stdout, remoteStdout)
	}

	if opts.Stderr && !e.tty {
		waitCount++
		headers.Set(api.StreamType, api.StreamTypeStderr)
		remoteStderr, err := conn.CreateStream(headers)
		if err != nil {
			return err
		}
		defer remoteStderr.Reset()
		go cp(api.StreamTypeStderr, e.stderr, remoteStderr)
	}

Loop:
	for {
		select {
		case <-doneChan:
			completedStreams++
			if completedStreams == waitCount {
				break Loop
			}
		case err := <-errorChan:
			return err
		}
	}

	return nil
}