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 }
// 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 }