// Handle output and command completion for a remote shell
Handle output and command completion for a remote shell
// Handle output and command completion for a remote shell
func (c *Client) handleSessionIO(action func(*ssh.Session) error) (string, error) {
	session, err := c.client.NewSession()
	if err != nil {
		return "", errors.Wrap(err, "failed to create new SSH session")
	}
	defer session.Close()
	stdOutPipe, err := session.StdoutPipe()
	if err != nil {
		return "", errors.Wrap(err, "failed to get pipe to standard output")
	}
	stdErrPipe, err := session.StderrPipe()
	if err != nil {
		return "", errors.Wrap(err, "failed to get pipe to standard error")
	}
	var stdOutErr bytes.Buffer
	outWriters := []io.Writer{&stdOutErr}
	errWriters := []io.Writer{&stdOutErr}
	if c.printOutputs {
		outWriters = append(outWriters, os.Stdout)
		errWriters = append(errWriters, os.Stderr)
	}
	stdOutWriter := io.MultiWriter(outWriters...)
	stdErrWriter := io.MultiWriter(errWriters...)
	err = action(session)
	var errStdOut, errStdErr error
	syncChan := make(chan bool)
	go func() {
		_, errStdOut = io.Copy(stdOutWriter, stdOutPipe)
		syncChan <- true
	}()
	go func() {
		_, errStdErr = io.Copy(stdErrWriter, stdErrPipe)
		syncChan <- true
	}()
	<-syncChan
	<-syncChan
	if err != nil {
		return stdOutErr.String(), errors.Wrap(err, "failed while remote executing")
	}
	if err := session.Wait(); err != nil {
		if err, ok := err.(*ssh.ExitError); ok {
			return stdOutErr.String(), &plan.RunError{ExitCode: err.ExitStatus()}
		}
		return stdOutErr.String(), errors.Wrap(err, "failed while waiting for end of remote execution")
	}
	if errStdOut != nil {
		return stdOutErr.String(), errors.Wrap(errStdOut, "failed while capturing stdout")
	}
	if errStdErr != nil {
		return stdOutErr.String(), errors.Wrap(errStdErr, "failed while capturing stderr")
	}
	return stdOutErr.String(), nil
}
func ( c * Client ) handleSessionIO ( action func ( * ssh . Session ) error ) ( string , error ) { session , err := c . client . NewSession ( ) if err != nil { return " " , errors . Wrap ( err , " " ) } defer session . Close ( ) stdOutPipe , err := session . StdoutPipe ( ) if err != nil { return " " , errors . Wrap ( err , " " ) } stdErrPipe , err := session . StderrPipe ( ) if err != nil { return " " , errors . Wrap ( err , " " ) } var stdOutErr bytes . Buffer outWriters := [ ] io . Writer { & stdOutErr } errWriters := [ ] io . Writer { & stdOutErr } if c . printOutputs { outWriters = append ( outWriters , os . Stdout ) errWriters = append ( errWriters , os . Stderr ) } stdOutWriter := io . MultiWriter ( outWriters ... ) stdErrWriter := io . MultiWriter ( errWriters ... ) err = action ( session ) var errStdOut , errStdErr error syncChan := make ( chan bool ) go func ( ) { _ , errStdOut = io . Copy ( stdOutWriter , stdOutPipe ) syncChan <- true } ( ) go func ( ) { _ , errStdErr = io . Copy ( stdErrWriter , stdErrPipe ) syncChan <- true } ( ) <- syncChan <- syncChan if err != nil { return stdOutErr . String ( ) , errors . Wrap ( err , " " ) } if err := session . Wait ( ) ; err != nil { if err , ok := err . ( * ssh . ExitError ) ; ok { return stdOutErr . String ( ) , & plan . RunError { ExitCode : err . ExitStatus ( ) } } return stdOutErr . String ( ) , errors . Wrap ( err , " " ) } if errStdOut != nil { return stdOutErr . String ( ) , errors . Wrap ( errStdOut , " " ) } if errStdErr != nil { return stdOutErr . String ( ) , errors . Wrap ( errStdErr , " " ) } return stdOutErr . String ( ) , nil }
Handle output and command completion for a remote shell
Handle output and command completion for a remote shell
// Write stdout and stderr to both this process' stdout and stderr, and // buffers, for later re-use. // Don't respond to err until output complete // Make sure copying is finished // Now we can return the error