func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (<-chan []byte, <-chan error, error) { rpipe, wpipe, err := vm.LongPipe() if err != nil { return nil, nil, err } inst.merger.Add("ssh", rpipe) args := append(inst.sshArgs("-p"), "root@localhost", command) if inst.cfg.Debug { Logf(0, "running command: ssh %#v", args) } cmd := exec.Command("ssh", args...) cmd.Stdout = wpipe cmd.Stderr = wpipe if err := cmd.Start(); err != nil { wpipe.Close() return nil, nil, err } wpipe.Close() errc := make(chan error, 1) signal := func(err error) { select { case errc <- err: default: } } go func() { select { case <-time.After(timeout): signal(vm.TimeoutErr) case <-stop: signal(vm.TimeoutErr) case err := <-inst.merger.Err: signal(err) } cmd.Process.Kill() cmd.Wait() }() return inst.merger.Output, errc, nil }
func ctorImpl(cfg *vm.Config) (vm.Instance, error) { inst := &instance{cfg: cfg} closeInst := inst defer func() { if closeInst != nil { closeInst.close(false) } }() if err := validateConfig(cfg); err != nil { return nil, err } if cfg.Image == "9p" { inst.cfg.Sshkey = filepath.Join(inst.cfg.Workdir, "key") keygen := exec.Command("ssh-keygen", "-t", "rsa", "-b", "2048", "-N", "", "-C", "", "-f", inst.cfg.Sshkey) if out, err := keygen.CombinedOutput(); err != nil { return nil, fmt.Errorf("failed to execute ssh-keygen: %v\n%s", err, out) } initFile := filepath.Join(cfg.Workdir, "init.sh") if err := ioutil.WriteFile(initFile, []byte(strings.Replace(initScript, "{{KEY}}", inst.cfg.Sshkey, -1)), 0777); err != nil { return nil, fmt.Errorf("failed to create init file: %v", err) } } var err error inst.rpipe, inst.wpipe, err = vm.LongPipe() if err != nil { return nil, err } if err := inst.Boot(); err != nil { return nil, err } closeInst = nil return inst, nil }
func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (<-chan []byte, <-chan error, error) { conRpipe, conWpipe, err := vm.LongPipe() if err != nil { return nil, nil, err } conAddr := fmt.Sprintf("%v.%v.%[email protected]", GCE.ProjectID, GCE.ZoneID, inst.name) conArgs := append(sshArgs(inst.gceKey, "-p", 9600), conAddr) con := exec.Command("ssh", conArgs...) con.Env = []string{} con.Stdout = conWpipe con.Stderr = conWpipe if _, err := con.StdinPipe(); err != nil { // SSH would close connection on stdin EOF conRpipe.Close() conWpipe.Close() return nil, nil, err } if err := con.Start(); err != nil { conRpipe.Close() conWpipe.Close() return nil, nil, fmt.Errorf("failed to connect to console server: %v", err) } conWpipe.Close() sshRpipe, sshWpipe, err := vm.LongPipe() if err != nil { con.Process.Kill() sshRpipe.Close() return nil, nil, err } if inst.sshUser != "root" { command = fmt.Sprintf("sudo bash -c '%v'", command) } args := append(sshArgs(inst.sshKey, "-p", 22), inst.sshUser+"@"+inst.name, command) ssh := exec.Command("ssh", args...) ssh.Stdout = sshWpipe ssh.Stderr = sshWpipe if err := ssh.Start(); err != nil { con.Process.Kill() conRpipe.Close() sshRpipe.Close() sshWpipe.Close() return nil, nil, fmt.Errorf("failed to connect to instance: %v", err) } sshWpipe.Close() merger := vm.NewOutputMerger(nil) merger.Add("console", conRpipe) merger.Add("ssh", sshRpipe) errc := make(chan error, 1) signal := func(err error) { select { case errc <- err: default: } } go func() { select { case <-time.After(timeout): signal(vm.TimeoutErr) case <-stop: signal(vm.TimeoutErr) case <-inst.closed: signal(fmt.Errorf("instance closed")) case err := <-merger.Err: // Check if the instance was terminated due to preemption or host maintenance. time.Sleep(5 * time.Second) // just to avoid any GCE races if !GCE.IsInstanceRunning(inst.name) { Logf(1, "%v: ssh exited but instance is not running", inst.name) err = vm.TimeoutErr } signal(err) } con.Process.Kill() ssh.Process.Kill() merger.Wait() con.Wait() ssh.Wait() }() return merger.Output, errc, nil }
func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (<-chan []byte, <-chan error, error) { tty, err := openConsole(inst.console) if err != nil { return nil, nil, err } adbRpipe, adbWpipe, err := vm.LongPipe() if err != nil { tty.Close() return nil, nil, err } if inst.cfg.Debug { Logf(0, "starting: adb shell %v", command) } adb := exec.Command(inst.cfg.Bin, "-s", inst.cfg.Device, "shell", "cd /data; "+command) adb.Stdout = adbWpipe adb.Stderr = adbWpipe if err := adb.Start(); err != nil { tty.Close() adbRpipe.Close() adbWpipe.Close() return nil, nil, fmt.Errorf("failed to start adb: %v", err) } adbWpipe.Close() var tee io.Writer if inst.cfg.Debug { tee = os.Stdout } merger := vm.NewOutputMerger(tee) merger.Add("console", tty) merger.Add("adb", adbRpipe) errc := make(chan error, 1) signal := func(err error) { select { case errc <- err: default: } } go func() { select { case <-time.After(timeout): signal(vm.TimeoutErr) case <-stop: signal(vm.TimeoutErr) case <-inst.closed: if inst.cfg.Debug { Logf(0, "instance closed") } signal(fmt.Errorf("instance closed")) case err := <-merger.Err: signal(err) } tty.Close() adb.Process.Kill() merger.Wait() adb.Wait() }() return merger.Output, errc, nil }
func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) { catRpipe, catWpipe, err := vm.LongPipe() if err != nil { return nil, nil, err } cat := exec.Command("cat", inst.console) cat.Stdout = catWpipe cat.Stderr = catWpipe if err := cat.Start(); err != nil { catRpipe.Close() catWpipe.Close() return nil, nil, fmt.Errorf("failed to start cat %v: %v", inst.console, err) } catWpipe.Close() catDone := make(chan error, 1) go func() { err := cat.Wait() if inst.cfg.Debug { Logf(0, "cat exited: %v", err) } catDone <- fmt.Errorf("cat exited: %v", err) }() adbRpipe, adbWpipe, err := vm.LongPipe() if err != nil { cat.Process.Kill() catRpipe.Close() return nil, nil, err } if inst.cfg.Debug { Logf(0, "starting: adb shell %v", command) } adb := exec.Command(inst.cfg.Bin, "-s", inst.cfg.Device, "shell", "cd /data; "+command) adb.Stdout = adbWpipe adb.Stderr = adbWpipe if err := adb.Start(); err != nil { cat.Process.Kill() catRpipe.Close() adbRpipe.Close() adbWpipe.Close() return nil, nil, fmt.Errorf("failed to start adb: %v", err) } adbWpipe.Close() adbDone := make(chan error, 1) go func() { err := adb.Wait() if inst.cfg.Debug { Logf(0, "adb exited: %v", err) } adbDone <- fmt.Errorf("adb exited: %v", err) }() var tee io.Writer if inst.cfg.Debug { tee = os.Stdout } merger := vm.NewOutputMerger(tee) merger.Add(catRpipe) merger.Add(adbRpipe) errc := make(chan error, 1) signal := func(err error) { select { case errc <- err: default: } } go func() { select { case <-time.After(timeout): signal(vm.TimeoutErr) cat.Process.Kill() adb.Process.Kill() case <-inst.closed: if inst.cfg.Debug { Logf(0, "instance closed") } signal(fmt.Errorf("instance closed")) cat.Process.Kill() adb.Process.Kill() case err := <-catDone: signal(err) adb.Process.Kill() case err := <-adbDone: signal(err) cat.Process.Kill() } merger.Wait() }() return merger.Output, errc, nil }
func (inst *instance) Run(timeout time.Duration, command string) (<-chan []byte, <-chan error, error) { conRpipe, conWpipe, err := vm.LongPipe() if err != nil { return nil, nil, err } conAddr := fmt.Sprintf("%v.%v.%[email protected]", GCE.ProjectID, GCE.ZoneID, inst.name) conArgs := append(sshArgs(inst.sshkey, "-p", 9600), conAddr) con := exec.Command("ssh", conArgs...) con.Env = []string{} con.Stdout = conWpipe con.Stderr = conWpipe if _, err := con.StdinPipe(); err != nil { // SSH would close connection on stdin EOF conRpipe.Close() conWpipe.Close() return nil, nil, err } if err := con.Start(); err != nil { conRpipe.Close() conWpipe.Close() return nil, nil, fmt.Errorf("failed to connect to console server: %v", err) } conWpipe.Close() conDone := make(chan error, 1) go func() { err := con.Wait() conDone <- fmt.Errorf("console connection closed: %v", err) }() sshRpipe, sshWpipe, err := vm.LongPipe() if err != nil { con.Process.Kill() sshRpipe.Close() return nil, nil, err } args := append(sshArgs(inst.cfg.Sshkey, "-p", 22), "root@"+inst.name, command) ssh := exec.Command("ssh", args...) ssh.Stdout = sshWpipe ssh.Stderr = sshWpipe if err := ssh.Start(); err != nil { con.Process.Kill() conRpipe.Close() sshRpipe.Close() sshWpipe.Close() return nil, nil, fmt.Errorf("failed to connect to instance: %v", err) } sshWpipe.Close() sshDone := make(chan error, 1) go func() { err := ssh.Wait() sshDone <- fmt.Errorf("ssh exited: %v", err) }() merger := vm.NewOutputMerger(nil) merger.Add(conRpipe) merger.Add(sshRpipe) errc := make(chan error, 1) signal := func(err error) { select { case errc <- err: default: } } go func() { select { case <-time.After(timeout): signal(vm.TimeoutErr) con.Process.Kill() ssh.Process.Kill() case <-inst.closed: signal(fmt.Errorf("instance closed")) con.Process.Kill() ssh.Process.Kill() case err := <-conDone: signal(err) ssh.Process.Kill() case err := <-sshDone: signal(err) con.Process.Kill() } merger.Wait() }() return merger.Output, errc, nil }