aboutsummaryrefslogtreecommitdiffstats
path: root/vm/gce
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-11-22 17:53:36 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-11-22 17:56:48 +0100
commit31af2ce0225268bd9d1ed27fef830debbed2a188 (patch)
tree039b9715ba93d7e256e57a4de62692366e66879e /vm/gce
parenta7bbe24b6f474dba5ca701413c268fe192e44346 (diff)
vm/gce: fix boot output capture
Turns out GetSerialPortOutput API does not work if instance has serial port connections enabled (which we always have). Get output from serial port relay service instead.
Diffstat (limited to 'vm/gce')
-rw-r--r--vm/gce/gce.go59
1 files changed, 52 insertions, 7 deletions
diff --git a/vm/gce/gce.go b/vm/gce/gce.go
index cf9009a10..96cc14e79 100644
--- a/vm/gce/gce.go
+++ b/vm/gce/gce.go
@@ -15,7 +15,6 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
- "errors"
"fmt"
"io"
"io/ioutil"
@@ -164,10 +163,7 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
sshUser = "syzkaller"
}
Logf(0, "wait instance to boot: %v (%v)", name, ip)
- if err := pool.waitInstanceBoot(ip, sshKey, sshUser); err != nil {
- if output, _ := pool.GCE.GetSerialPortOutput(name); len(output) != 0 {
- err = errors.New(err.Error() + "\n\n" + output)
- }
+ if err := pool.waitInstanceBoot(name, ip, sshKey, sshUser, gceKey); err != nil {
return nil, err
}
ok = true
@@ -350,7 +346,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin
return merger.Output, errc, nil
}
-func (pool *Pool) waitInstanceBoot(ip, sshKey, sshUser string) error {
+func (pool *Pool) waitInstanceBoot(name, ip, sshKey, sshUser, gceKey string) error {
pwd := "pwd"
if pool.env.OS == "windows" {
pwd = "dir"
@@ -364,7 +360,56 @@ func (pool *Pool) waitInstanceBoot(ip, sshKey, sshUser string) error {
return nil
}
}
- return fmt.Errorf("can't ssh into the instance")
+ output, err := pool.getSerialPortOutput(name, gceKey)
+ if err != nil {
+ output = []byte(fmt.Sprintf("failed to get boot output: %v", err))
+ }
+ return fmt.Errorf("can't ssh into the instance\n\n%s", output)
+}
+
+func (pool *Pool) getSerialPortOutput(name, gceKey string) ([]byte, error) {
+ conRpipe, conWpipe, err := osutil.LongPipe()
+ if err != nil {
+ return nil, err
+ }
+ defer conRpipe.Close()
+ defer conWpipe.Close()
+ conAddr := fmt.Sprintf("%v.%v.%v.syzkaller.port=1.replay-lines=10000@ssh-serialport.googleapis.com",
+ pool.GCE.ProjectID, pool.GCE.ZoneID, name)
+ conArgs := append(sshArgs(pool.env.Debug, gceKey, "-p", 9600), conAddr)
+ con := osutil.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
+ return nil, err
+ }
+ if err := con.Start(); err != nil {
+ return nil, fmt.Errorf("failed to connect to console server: %v", err)
+ }
+ conWpipe.Close()
+ done := make(chan bool)
+ go func() {
+ timeout := time.NewTimer(time.Minute)
+ defer timeout.Stop()
+ select {
+ case <-done:
+ case <-timeout.C:
+ }
+ con.Process.Kill()
+ }()
+ var output []byte
+ buf := make([]byte, 64<<10)
+ for {
+ n, err := conRpipe.Read(buf)
+ if err != nil || n == 0 {
+ break
+ }
+ output = append(output, buf[:n]...)
+ }
+ close(done)
+ con.Wait()
+ return output, nil
}
func uploadImageToGCS(localImage, gcsImage string) error {