| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // The debugnewvm command creates and destroys a VM-based GCE buildlet |
| // with lots of logging for debugging. Nothing depends on this. |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "fmt" |
| "log" |
| "net/http" |
| "strings" |
| "time" |
| |
| "golang.org/x/build/buildenv" |
| "golang.org/x/build/buildlet" |
| "golang.org/x/build/dashboard" |
| "golang.org/x/oauth2" |
| "golang.org/x/oauth2/google" |
| compute "google.golang.org/api/compute/v1" |
| ) |
| |
| var ( |
| hostType = flag.String("host", "host-windows-amd64-2012", "host type to create") |
| vmImage = flag.String("override-image", "", "if non-empty, an alternate GCE VM image to use, overriding the one defined for the given host") |
| serial = flag.Bool("serial", true, "watch serial") |
| pauseAfterUp = flag.Bool("pause-after-up", false, "pause for a few seconds (enough to SIGSTOP) after WorkDir returns") |
| ) |
| |
| var ( |
| computeSvc *compute.Service |
| env *buildenv.Environment |
| ) |
| |
| func main() { |
| buildenv.RegisterFlags() |
| flag.Parse() |
| |
| hconf, ok := dashboard.Hosts[*hostType] |
| if !ok { |
| log.Fatalf("unknown host type %q", *hostType) |
| } |
| if !hconf.IsGCE() { |
| log.Fatalf("host type %q is not a GCE host type", *hostType) |
| } |
| if *vmImage != "" { |
| hconf.VMImage = *vmImage |
| } |
| |
| ts, err := google.DefaultTokenSource(context.Background()) |
| if err != nil { |
| log.Fatal(err) |
| } |
| env = buildenv.FromFlags() |
| computeSvc, _ = compute.New(oauth2.NewClient(context.TODO(), ts)) |
| |
| name := fmt.Sprintf("debug-temp-%d", time.Now().Unix()) |
| log.Printf("Creating %s (with VM image %q)", name, hconf.VMImage) |
| c, err := buildlet.StartNewVM(ts, name, *hostType, buildlet.VMOpts{ |
| Zone: env.Zone, |
| ProjectID: env.ProjectName, |
| DeleteIn: 15 * time.Minute, |
| OnInstanceRequested: func() { log.Printf("instance requested") }, |
| OnInstanceCreated: func() { |
| log.Printf("instance created") |
| if *serial { |
| go watchSerial(name) |
| } |
| }, |
| OnGotInstanceInfo: func() { log.Printf("got instance info") }, |
| OnBeginBuildletProbe: func(buildletURL string) { |
| log.Printf("About to hit %s to see if buildlet is up yet...", buildletURL) |
| }, |
| OnEndBuildletProbe: func(res *http.Response, err error) { |
| if err != nil { |
| log.Printf("client buildlet probe error: %v", err) |
| return |
| } |
| log.Printf("buildlet probe: %s", res.Status) |
| }, |
| }) |
| if err != nil { |
| log.Fatalf("StartNewVM: %v", err) |
| } |
| dir, err := c.WorkDir() |
| log.Printf("WorkDir: %v, %v", dir, err) |
| if *pauseAfterUp { |
| log.Printf("Shutting down in 5 seconds...") |
| time.Sleep(5 * time.Second) |
| } |
| if err := c.Close(); err != nil { |
| log.Fatalf("Close: %v", err) |
| } |
| log.Printf("done.") |
| time.Sleep(5 * time.Second) // wait for serial logging to catch up |
| } |
| |
| // watchSerial streams the named VM's serial port to log.Printf. It's roughly: |
| // gcloud compute connect-to-serial-port --zone=xxx $NAME |
| // but in Go and works. For some reason, gcloud doesn't work as a |
| // child process and has weird errors. |
| func watchSerial(name string) { |
| start := int64(0) |
| indent := strings.Repeat(" ", len("2017/07/25 06:37:14 SERIAL: ")) |
| for { |
| sout, err := computeSvc.Instances.GetSerialPortOutput(env.ProjectName, env.Zone, name).Start(start).Do() |
| if err != nil { |
| log.Printf("serial output error: %v", err) |
| return |
| } |
| moved := sout.Next != start |
| start = sout.Next |
| contents := strings.Replace(strings.TrimSpace(sout.Contents), "\r\n", "\r\n"+indent, -1) |
| if contents != "" { |
| log.Printf("SERIAL: %s", contents) |
| } |
| if !moved { |
| time.Sleep(1 * time.Second) |
| } |
| } |
| } |