scripts/remote.go (148 lines of code) (raw):

package main /* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved */ import ( "fmt" "io/ioutil" "log" "net" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "github.com/pkg/sftp" "golang.org/x/crypto/ssh" "github.com/spf13/pflag" ) func binName() string { if runtime.GOOS == "windows" { return "go2chef.exe" } return "go2chef" } var ( targets = pflag.StringArrayP("target", "T", []string{}, "target to remotely execute on") username = pflag.StringP("username", "u", "root", "username to connect as") password = pflag.StringP("password", "p", "root", "password to connect with") binary = pflag.StringP("binary", "b", fmt.Sprintf("build/%s/%s/%s", runtime.GOOS, runtime.GOARCH, binName()), "binary to copy") port = pflag.StringP("port", "P", "22", "port") windows = pflag.BoolP("windows", "W", false, "enable windows mode") cfg = pflag.StringP("config", "c", "", "config to copy") bundles = pflag.StringArrayP("bundle", "B", []string{}, "bundles to copy") ) func main() { pflag.Parse() config := &ssh.ClientConfig{ User: *username, Auth: []ssh.AuthMethod{ ssh.Password(*password), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } if *windows && !strings.HasSuffix(*binary, ".exe") { *binary = *binary + ".exe" } var wg sync.WaitGroup for _, target := range *targets { wg.Add(1) go func(t string) { defer wg.Done() doTarget(t, config) }(target) } wg.Wait() } func doTarget(target string, config *ssh.ClientConfig) { client, err := ssh.Dial("tcp", net.JoinHostPort(target, *port), config) if err != nil { log.Printf("ERROR: failed to connect to %s: %s", target, err) return } defer client.Close() c, err := sftp.NewClient(client) if err != nil { log.Printf("ERROR: failed to establish sftp to %s: %s", target, err) return } defer c.Close() tmp, err := ioutil.TempDir("", "") if err != nil { log.Printf("ERROR: failed to create temp dir: %s", err) return } binPath, err := copySFTP(c, tmp, *binary, 0755) if err != nil { log.Printf("ERROR: failed to copy sftp to %s: %s", target, err) return } log.Printf("copied %s to %s", *binary, binPath) cfgPath, err := copySFTP(c, tmp, *cfg, 0644) if err != nil { log.Printf("ERROR: failed to copy sftp to %s: %s", target, err) return } log.Printf("copied %s to %s", *cfg, cfgPath) for _, b := range *bundles { bPath, err := copyBundle(c, tmp, b) if err != nil { log.Printf("ERROR: failed to copy sftp to %s: %s", b, err) return } log.Printf("copied %s to %s", b, bPath) } session, err := client.NewSession() if err != nil { log.Printf("ERROR: failed to create session on %s: %s", target, err) return } defer session.Close() session.Stderr = os.Stderr session.Stdout = os.Stdout if err := session.Run( fmt.Sprintf( "cd %s && %s --log-level DEBUG --local-config %s", filepath.Dir(binPath), binPath, cfgPath, ), ); err != nil { log.Printf("ERROR: failed to run go2chef remotely: %s", err) return } } func copySFTP(c *sftp.Client, tmp string, path string, mode os.FileMode) (string, error) { if *windows { tmp = filepath.Join("/", filepath.Base(tmp)) } realPath := filepath.Join(tmp, filepath.Base(path)) if err := c.MkdirAll(tmp); err != nil { return "", err } data, err := ioutil.ReadFile(path) if err != nil { return "", err } f, err := c.OpenFile(realPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR) if err != nil { return "", err } defer func() { _ = f.Close() }() _ = f.Chmod(mode) if _, err := f.Write(data); err != nil { return "", err } return realPath, nil } func copyBundle(c *sftp.Client, ctmp string, path string) (string, error) { tmp, err := ioutil.TempDir("", "") if err != nil { return "", err } rName := filepath.Join(tmp, filepath.Base(path)) + ".tar.gz" cmd := exec.Command("tar", "czvf", rName, "./") cmd.Dir = path cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return "", err } return copySFTP(c, ctmp, rName, 0644) }