New returns net.Conn
(ctx context.Context, cmd string, args ...string)
| 33 | |
| 34 | // New returns net.Conn |
| 35 | func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) { |
| 36 | // Don't kill the ssh process if the context is cancelled. Killing the |
| 37 | // ssh process causes an error when go's http.Client tries to reuse the |
| 38 | // net.Conn (commandConn). |
| 39 | // |
| 40 | // Not passing down the Context might seem counter-intuitive, but in this |
| 41 | // case, the lifetime of the process should be managed by the http.Client, |
| 42 | // not the caller's Context. |
| 43 | // |
| 44 | // Further details;; |
| 45 | // |
| 46 | // - https://github.com/docker/cli/pull/3900 |
| 47 | // - https://github.com/docker/compose/issues/9448#issuecomment-1264263721 |
| 48 | ctx = context.WithoutCancel(ctx) |
| 49 | c := commandConn{cmd: exec.CommandContext(ctx, cmd, args...)} |
| 50 | // we assume that args never contains sensitive information |
| 51 | logrus.Debugf("commandconn: starting %s with %v", cmd, args) |
| 52 | c.cmd.Env = os.Environ() |
| 53 | c.cmd.SysProcAttr = &syscall.SysProcAttr{} |
| 54 | setPdeathsig(c.cmd) |
| 55 | createSession(c.cmd) |
| 56 | var err error |
| 57 | c.stdin, err = c.cmd.StdinPipe() |
| 58 | if err != nil { |
| 59 | return nil, err |
| 60 | } |
| 61 | c.stdout, err = c.cmd.StdoutPipe() |
| 62 | if err != nil { |
| 63 | return nil, err |
| 64 | } |
| 65 | c.cmd.Stderr = &stderrWriter{ |
| 66 | stderrMu: &c.stderrMu, |
| 67 | stderr: &c.stderr, |
| 68 | debugPrefix: fmt.Sprintf("commandconn (%s):", cmd), |
| 69 | } |
| 70 | c.localAddr = dummyAddr{network: "dummy", s: "dummy-0"} |
| 71 | c.remoteAddr = dummyAddr{network: "dummy", s: "dummy-1"} |
| 72 | return &c, c.cmd.Start() |
| 73 | } |
| 74 | |
| 75 | // commandConn implements net.Conn |
| 76 | type commandConn struct { |