in plugins/teststeps/exec/transport/ssh_process_async.go [67:144]
func (spa *sshProcessAsync) Start(ctx xcontext.Context) error {
errChan := make(chan error, 1)
resChan := make(chan string, 1)
go func() {
// NOTE: golang doesnt support forking, so the started process needs to be
// forcefully detached by closing the ssh session; detach is defered here
client, err := ssh.Dial("tcp", spa.addr, spa.clientConfig)
if err != nil {
errChan <- fmt.Errorf("cannot connect to SSH server %s: %v", spa.addr, err)
return
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
errChan <- fmt.Errorf("cannot create SSH session to server: %v", err)
return
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
errChan <- fmt.Errorf("failed to get stdout pipe")
return
}
ctx.Debugf("starting remote agent: %s", spa.cmd)
if err := session.Start(spa.cmd); err != nil {
errChan <- fmt.Errorf("failed to start process: %w", err)
return
}
// read the session id that the agent will put on stdout
s := bufio.NewScanner(stdout)
if !s.Scan() {
errChan <- fmt.Errorf("agent did not return a session id")
return
}
resChan <- s.Text()
}()
select {
case err := <-errChan:
return err
case sid := <-resChan:
ctx.Debugf("remote sid: %s", sid)
outWriter := spa.outWriter
if outWriter == nil {
var err error
outWriter, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err != nil {
return err
}
}
errWriter := spa.errWriter
if errWriter == nil {
var err error
errWriter, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err != nil {
return err
}
}
mon := &asyncMonitor{spa.addr, spa.clientConfig, spa.agent, sid}
go mon.Start(ctx, outWriter, errWriter, spa.exitChan)
return nil
case <-time.After(5 * time.Second):
return fmt.Errorf("timeout while starting agent")
case <-ctx.Done():
return ctx.Err()
}
}