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) Boot() error { for { // Find an unused TCP port. inst.port = rand.Intn(64<<10-1<<10) + 1<<10 ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%v", inst.port)) if err == nil { ln.Close() break } } // TODO: ignores inst.cfg.Cpu args := []string{ "-m", strconv.Itoa(inst.cfg.Mem), "-net", "nic", "-net", fmt.Sprintf("user,host=%v,hostfwd=tcp::%v-:22", hostAddr, inst.port), "-display", "none", "-serial", "stdio", "-no-reboot", "-enable-kvm", "-numa", "node,nodeid=0,cpus=0-1", "-numa", "node,nodeid=1,cpus=2-3", "-smp", "sockets=2,cores=2,threads=1", "-usb", "-usbdevice", "mouse", "-usbdevice", "tablet", "-soundhw", "all", } if inst.cfg.Image == "9p" { args = append(args, "-fsdev", "local,id=fsdev0,path=/,security_model=none,readonly", "-device", "virtio-9p-pci,fsdev=fsdev0,mount_tag=/dev/root", ) } else { args = append(args, "-hda", inst.cfg.Image, "-snapshot", ) } if inst.cfg.Initrd != "" { args = append(args, "-initrd", inst.cfg.Initrd, ) } if inst.cfg.Kernel != "" { cmdline := "console=ttyS0 vsyscall=native rodata=n oops=panic panic_on_warn=1 panic=-1" + " ftrace_dump_on_oops=orig_cpu earlyprintk=serial slub_debug=UZ " if inst.cfg.Image == "9p" { cmdline += "root=/dev/root rootfstype=9p rootflags=trans=virtio,version=9p2000.L,cache=loose " cmdline += "init=" + filepath.Join(inst.cfg.Workdir, "init.sh") + " " } else { cmdline += "root=/dev/sda " } args = append(args, "-kernel", inst.cfg.Kernel, "-append", cmdline+inst.cfg.Cmdline, ) } qemu := exec.Command(inst.cfg.Bin, args...) qemu.Stdout = inst.wpipe qemu.Stderr = inst.wpipe if err := qemu.Start(); err != nil { return fmt.Errorf("failed to start %v %+v: %v", inst.cfg.Bin, args, err) } inst.wpipe.Close() inst.wpipe = nil inst.qemu = qemu // Qemu has started. // Start output merger. var tee io.Writer if inst.cfg.Debug { tee = os.Stdout } inst.merger = vm.NewOutputMerger(tee) inst.merger.Add(inst.rpipe) inst.rpipe = nil var bootOutput []byte bootOutputStop := make(chan bool) go func() { for { select { case out := <-inst.merger.Output: bootOutput = append(bootOutput, out...) case <-bootOutputStop: close(bootOutputStop) return } } }() // Wait for the qemu asynchronously. inst.waiterC = make(chan error, 1) go func() { err := qemu.Wait() inst.waiterC <- err }() // Wait for ssh server to come up. time.Sleep(10 * time.Second) start := time.Now() for { c, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%v", inst.port), 3*time.Second) if err == nil { c.SetDeadline(time.Now().Add(3 * time.Second)) var tmp [1]byte n, err := c.Read(tmp[:]) c.Close() if err == nil && n > 0 { break // ssh is up and responding } time.Sleep(3 * time.Second) } select { case err := <-inst.waiterC: inst.waiterC <- err // repost it for Close time.Sleep(time.Second) // wait for any pending output bootOutputStop <- true <-bootOutputStop return fmt.Errorf("qemu stopped:\n%v\n", string(bootOutput)) default: } if time.Since(start) > 10*time.Minute { bootOutputStop <- true <-bootOutputStop return fmt.Errorf("ssh server did not start:\n%v\n", string(bootOutput)) } } bootOutputStop <- true return 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 }