diff --git a/cmd/pilosactl/main.go b/cmd/pilosactl/main.go index e2db1c39e..d5b36153d 100644 --- a/cmd/pilosactl/main.go +++ b/cmd/pilosactl/main.go @@ -13,7 +13,6 @@ import ( "log" "math/rand" "os" - "os/exec" "os/signal" "path" "path/filepath" @@ -1374,10 +1373,6 @@ func (cmd *BspawnCommand) ParseFlags(args []string) error { if err != nil { return err } - - // handle pilosa creation - // handle agent creation - return nil } @@ -1432,43 +1427,22 @@ func (cmd *BspawnCommand) Run(ctx context.Context) error { } } -func copyBinary(agentConnections []*pilosactl.SSH, pkg, goos, goarch string) error { - binName := path.Base(pkg) - binLoc := path.Join(os.TempDir(), binName) - com := exec.Command("go", "build", "-o", binLoc, pkg) - com.Env = append([]string{"GOOS=" + goos, "GOARCH=" + goarch}, os.Environ()...) - - err := com.Run() +func copyBinary(fleet pilosactl.SSHFleet, pkg, goos, goarch string) error { + bin, err := pilosactl.BuildBinary(pkg, goos, goarch) if err != nil { return err } - agentWriters := make([]io.Writer, len(agentConnections)) - for i, conn := range agentConnections { - agentWriters[i], err = conn.OpenFile(binName, "+x") - if err != nil { - return err - } - } - - f, err := os.Open(binLoc) - if err != nil { - return err - } - _, err = io.Copy(io.MultiWriter(agentWriters...), f) + wc, err := fleet.OpenFile(path.Base(pkg), "+x") if err != nil { return err } - for _, w := range agentWriters { - if wc, ok := w.(io.WriteCloser); ok { - err = wc.Close() - if err != nil { - return err - } - } + _, err = io.Copy(wc, bin) + if err != nil { + return err } - return nil + return wc.Close() } func (cmd *BspawnCommand) spawnRemote(ctx context.Context, runUUID uuid.UUID) error { diff --git a/pilosactl/build.go b/pilosactl/build.go new file mode 100644 index 000000000..d69a89335 --- /dev/null +++ b/pilosactl/build.go @@ -0,0 +1,29 @@ +package pilosactl + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" +) + +func BuildBinary(pkg, goos, goarch string) (io.Reader, error) { + binFile, err := ioutil.TempFile("", "pilosactl") + if err != nil { + return nil, fmt.Errorf("build binary: %v", err) + } + com := exec.Command("go", "build", "-o", binFile.Name(), pkg) + com.Env = append([]string{"GOOS=" + goos, "GOARCH=" + goarch}, os.Environ()...) + + err = com.Run() + if err != nil { + return nil, err + } + + f, err := os.Open(binFile.Name()) + if err != nil { + return nil, err + } + return f, nil +} diff --git a/pilosactl/ssh.go b/pilosactl/ssh.go index a42b776fc..4e6074157 100644 --- a/pilosactl/ssh.go +++ b/pilosactl/ssh.go @@ -8,6 +8,8 @@ import ( "os/user" "strings" + "errors" + "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" ) @@ -57,7 +59,9 @@ func NewSSH(host, username, keyfile string, stderr io.Writer) (*SSH, error) { return &SSH{client: client, Stderr: stderr}, nil } -func SSHClients(hosts []string, username, keyfile string, stderr io.Writer) ([]*SSH, error) { +type SSHFleet []*SSH + +func SSHClients(hosts []string, username, keyfile string, stderr io.Writer) (SSHFleet, error) { clients := make([]*SSH, len(hosts)) for i, host := range hosts { client, err := NewSSH(host, username, keyfile, stderr) @@ -96,7 +100,7 @@ func (r *remoteFile) Close() error { // will be passed directly to chmod to set the file permissions. rm, touch, // chmod, cat and support for semicolons, double ampersand, and output // redirection (>>) must be available in the remote shell. -func (s *SSH) OpenFile(name string, perm string) (io.WriteCloser, error) { +func (s *SSH) OpenFile(name, perm string) (io.WriteCloser, error) { sess, err := s.NewSession() if err != nil { return nil, err @@ -115,3 +119,51 @@ func (s *SSH) OpenFile(name string, perm string) (io.WriteCloser, error) { return &remoteFile{w: w, sess: sess}, nil } + +type multiWriteCloser struct { + wcs []io.WriteCloser + ws []io.Writer +} + +func newMultiWriteCloser() *multiWriteCloser { + return &multiWriteCloser{ + wcs: make([]io.WriteCloser, 0), + ws: make([]io.Writer, 0), + } +} + +func (mwc *multiWriteCloser) add(wc io.WriteCloser) { + mwc.wcs = append(mwc.wcs, wc) + mwc.ws = append(mwc.ws, wc) +} + +func (mwc *multiWriteCloser) Write(p []byte) (n int, err error) { + mw := io.MultiWriter(mwc.ws...) + return mw.Write(p) +} + +func (mwc *multiWriteCloser) Close() error { + errStr := "" + for _, wc := range mwc.wcs { + err := wc.Close() + if err != nil { + errStr = errStr + "; " + err.Error() + } + } + if errStr != "" { + return errors.New(errStr) + } + return nil +} + +func (sf SSHFleet) OpenFile(name, perm string) (io.WriteCloser, error) { + writers := newMultiWriteCloser() + for _, cli := range sf { + wc, err := cli.OpenFile(name, "+x") + if err != nil { + return nil, err + } + writers.add(wc) + } + return writers, nil +}