2015-01-16 03:29:16 +03:00
|
|
|
// Copyright 2015 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.
|
|
|
|
|
|
|
|
package buildlet
|
|
|
|
|
|
|
|
import (
|
2017-04-15 20:03:18 +03:00
|
|
|
"context"
|
2015-01-16 03:29:16 +03:00
|
|
|
"crypto/tls"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2015-06-05 04:25:50 +03:00
|
|
|
"log"
|
2015-01-16 03:29:16 +03:00
|
|
|
"net/http"
|
2018-05-04 05:42:17 +03:00
|
|
|
"sort"
|
2015-01-16 03:29:16 +03:00
|
|
|
"strings"
|
2018-05-04 05:42:17 +03:00
|
|
|
"sync"
|
2015-01-16 03:29:16 +03:00
|
|
|
"time"
|
|
|
|
|
2016-02-15 02:59:59 +03:00
|
|
|
"golang.org/x/build/buildenv"
|
2015-01-21 09:25:37 +03:00
|
|
|
"golang.org/x/build/dashboard"
|
2015-01-16 03:29:16 +03:00
|
|
|
"golang.org/x/oauth2"
|
2018-05-04 04:42:01 +03:00
|
|
|
"golang.org/x/oauth2/google"
|
2015-01-16 03:29:16 +03:00
|
|
|
"google.golang.org/api/compute/v1"
|
|
|
|
)
|
|
|
|
|
2015-03-21 02:14:52 +03:00
|
|
|
// GCEGate optionally specifies a function to run before any GCE API call.
|
|
|
|
// It's intended to be used to bound QPS rate to GCE.
|
|
|
|
var GCEGate func()
|
|
|
|
|
|
|
|
func apiGate() {
|
|
|
|
if GCEGate != nil {
|
|
|
|
GCEGate()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-16 20:54:03 +03:00
|
|
|
// VMOpts control how new VMs are started.
|
2015-01-16 03:29:16 +03:00
|
|
|
type VMOpts struct {
|
2018-05-04 05:42:17 +03:00
|
|
|
// Zone is the GCE zone to create the VM in.
|
|
|
|
// Optional; defaults to provided build environment's zone.
|
2015-01-16 03:29:16 +03:00
|
|
|
Zone string
|
|
|
|
|
2018-05-04 05:42:17 +03:00
|
|
|
// ProjectID is the GCE project ID (e.g. "foo-bar-123", not
|
|
|
|
// the numeric ID).
|
|
|
|
// Optional; defaults to provided build environment's project ID ("name").
|
2015-01-16 03:29:16 +03:00
|
|
|
ProjectID string
|
|
|
|
|
|
|
|
// TLS optionally specifies the TLS keypair to use.
|
|
|
|
// If zero, http without auth is used.
|
|
|
|
TLS KeyPair
|
|
|
|
|
|
|
|
// Optional description of the VM.
|
|
|
|
Description string
|
|
|
|
|
|
|
|
// Optional metadata to put on the instance.
|
|
|
|
Meta map[string]string
|
|
|
|
|
|
|
|
// DeleteIn optionally specifies a duration at which
|
|
|
|
// to delete the VM.
|
2018-05-04 05:42:17 +03:00
|
|
|
// If zero, a reasonable default is used.
|
|
|
|
// Negative means no deletion timeout.
|
2015-01-16 03:29:16 +03:00
|
|
|
DeleteIn time.Duration
|
|
|
|
|
|
|
|
// OnInstanceRequested optionally specifies a hook to run synchronously
|
|
|
|
// after the computeService.Instances.Insert call, but before
|
|
|
|
// waiting for its operation to proceed.
|
|
|
|
OnInstanceRequested func()
|
|
|
|
|
|
|
|
// OnInstanceCreated optionally specifies a hook to run synchronously
|
|
|
|
// after the instance operation succeeds.
|
|
|
|
OnInstanceCreated func()
|
|
|
|
|
|
|
|
// OnInstanceCreated optionally specifies a hook to run synchronously
|
|
|
|
// after the computeService.Instances.Get call.
|
|
|
|
OnGotInstanceInfo func()
|
2015-06-05 04:25:50 +03:00
|
|
|
|
2017-07-25 09:55:03 +03:00
|
|
|
// OnBeginBuildletProbe optionally specifies a hook to run synchronously
|
|
|
|
// before StartNewVM tries to hit buildletURL to see if it's up yet.
|
|
|
|
OnBeginBuildletProbe func(buildletURL string)
|
|
|
|
|
|
|
|
// OnEndBuildletProbe optionally specifies a hook to run synchronously
|
|
|
|
// after StartNewVM tries to hit the buildlet's URL to see if it's up.
|
|
|
|
// The hook parameters are the return values from http.Get.
|
|
|
|
OnEndBuildletProbe func(*http.Response, error)
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// StartNewVM boots a new VM on GCE and returns a buildlet client
|
|
|
|
// configured to speak to it.
|
2018-05-04 05:42:17 +03:00
|
|
|
func StartNewVM(creds *google.Credentials, buildEnv *buildenv.Environment, instName, hostType string, opts VMOpts) (*Client, error) {
|
|
|
|
ctx := context.TODO()
|
|
|
|
computeService, _ := compute.New(oauth2.NewClient(ctx, creds.TokenSource))
|
|
|
|
|
|
|
|
if opts.Description == "" {
|
|
|
|
opts.Description = fmt.Sprintf("Go Builder for %s", hostType)
|
|
|
|
}
|
|
|
|
if opts.ProjectID == "" {
|
|
|
|
opts.ProjectID = buildEnv.ProjectName
|
|
|
|
}
|
|
|
|
if opts.Zone == "" {
|
|
|
|
opts.Zone = buildEnv.Zone
|
|
|
|
}
|
|
|
|
if opts.DeleteIn == 0 {
|
|
|
|
opts.DeleteIn = 30 * time.Minute
|
|
|
|
}
|
2015-01-16 03:29:16 +03:00
|
|
|
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
hconf, ok := dashboard.Hosts[hostType]
|
2015-01-16 03:29:16 +03:00
|
|
|
if !ok {
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
return nil, fmt.Errorf("invalid host type %q", hostType)
|
|
|
|
}
|
2018-05-04 05:42:17 +03:00
|
|
|
if !hconf.IsVM() && !hconf.IsContainer() {
|
|
|
|
return nil, fmt.Errorf("host %q is type %q; want either a VM or container type", hostType, hconf.PoolName())
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
zone := opts.Zone
|
|
|
|
if zone == "" {
|
|
|
|
// TODO: automatic? maybe that's not useful.
|
|
|
|
// For now just return an error.
|
|
|
|
return nil, errors.New("buildlet: missing required Zone option")
|
|
|
|
}
|
|
|
|
projectID := opts.ProjectID
|
|
|
|
if projectID == "" {
|
|
|
|
return nil, errors.New("buildlet: missing required ProjectID option")
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix := "https://www.googleapis.com/compute/v1/projects/" + projectID
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
machType := prefix + "/zones/" + zone + "/machineTypes/" + hconf.MachineType()
|
2015-03-21 02:14:52 +03:00
|
|
|
diskType := "https://www.googleapis.com/compute/v1/projects/" + projectID + "/zones/" + zone + "/diskTypes/pd-ssd"
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
if hconf.RegularDisk {
|
2015-03-21 02:14:52 +03:00
|
|
|
diskType = "" // a spinning disk
|
|
|
|
}
|
2015-01-16 03:29:16 +03:00
|
|
|
|
2015-06-05 04:25:50 +03:00
|
|
|
// Request an IP address if this is a world-facing buildlet.
|
|
|
|
var accessConfigs []*compute.AccessConfig
|
|
|
|
// TODO(bradfitz): remove the "true ||" part once we figure out why the buildlet
|
|
|
|
// never boots without an IP address. Userspace seems to hang before we get to the buildlet?
|
|
|
|
if true || !opts.TLS.IsZero() {
|
|
|
|
accessConfigs = []*compute.AccessConfig{
|
|
|
|
&compute.AccessConfig{
|
|
|
|
Type: "ONE_TO_ONE_NAT",
|
|
|
|
Name: "External NAT",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-04 05:42:17 +03:00
|
|
|
srcImage := "https://www.googleapis.com/compute/v1/projects/" + projectID + "/global/images/" + hconf.VMImage
|
|
|
|
if hconf.IsContainer() {
|
|
|
|
var err error
|
|
|
|
srcImage, err = cosImage(ctx, computeService)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error find Container-Optimized OS image: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-16 03:29:16 +03:00
|
|
|
instance := &compute.Instance{
|
|
|
|
Name: instName,
|
|
|
|
Description: opts.Description,
|
|
|
|
MachineType: machType,
|
|
|
|
Disks: []*compute.AttachedDisk{
|
|
|
|
{
|
|
|
|
AutoDelete: true,
|
|
|
|
Boot: true,
|
|
|
|
Type: "PERSISTENT",
|
|
|
|
InitializeParams: &compute.AttachedDiskInitializeParams{
|
|
|
|
DiskName: instName,
|
2018-05-04 05:42:17 +03:00
|
|
|
SourceImage: srcImage,
|
2015-03-21 02:14:52 +03:00
|
|
|
DiskType: diskType,
|
2015-01-16 03:29:16 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Tags: &compute.Tags{
|
|
|
|
// Warning: do NOT list "http-server" or "allow-ssh" (our
|
|
|
|
// project's custom tag to allow ssh access) here; the
|
|
|
|
// buildlet provides full remote code execution.
|
|
|
|
// The https-server is authenticated, though.
|
|
|
|
Items: []string{"https-server"},
|
|
|
|
},
|
2015-01-16 23:59:14 +03:00
|
|
|
Metadata: &compute.Metadata{},
|
2015-01-16 03:29:16 +03:00
|
|
|
NetworkInterfaces: []*compute.NetworkInterface{
|
|
|
|
&compute.NetworkInterface{
|
2015-06-05 04:25:50 +03:00
|
|
|
AccessConfigs: accessConfigs,
|
|
|
|
Network: prefix + "/global/networks/default",
|
2015-01-16 03:29:16 +03:00
|
|
|
},
|
|
|
|
},
|
2018-05-05 20:13:05 +03:00
|
|
|
|
|
|
|
// Prior to git rev 1b1e086fd, we used preemptible
|
|
|
|
// instances, as we were helping test the feature. It was
|
|
|
|
// removed after git rev a23395d because we hadn't been
|
|
|
|
// using it for some time. Our VMs are so short-lived that
|
|
|
|
// the feature doesn't really help anyway. But if we ever
|
|
|
|
// find we want it again, this comment is here to point to
|
|
|
|
// code that might be useful to partially resurrect.
|
|
|
|
Scheduling: &compute.Scheduling{Preemptible: false},
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
2018-05-04 05:42:17 +03:00
|
|
|
|
|
|
|
// Container builders use the COS image, which defaults to
|
|
|
|
// logging to Cloud Logging, which requires the default
|
|
|
|
// service account. So enable it when needed.
|
|
|
|
// TODO: reduce this scope in the future, when we go wild with IAM.
|
|
|
|
if hconf.IsContainer() {
|
|
|
|
instance.ServiceAccounts = []*compute.ServiceAccount{
|
|
|
|
{
|
|
|
|
// This funky email address is the
|
|
|
|
// "default service account" for GCE VMs:
|
|
|
|
Email: fmt.Sprintf("%v-compute@developer.gserviceaccount.com", buildEnv.ProjectNumber),
|
|
|
|
Scopes: []string{compute.CloudPlatformScope},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-16 23:59:14 +03:00
|
|
|
addMeta := func(key, value string) {
|
|
|
|
instance.Metadata.Items = append(instance.Metadata.Items, &compute.MetadataItems{
|
|
|
|
Key: key,
|
2015-09-03 20:27:24 +03:00
|
|
|
Value: &value,
|
2015-01-16 23:59:14 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
// The buildlet-binary-url is the URL of the buildlet binary
|
|
|
|
// which the VMs are configured to download at boot and run.
|
|
|
|
// This lets us/ update the buildlet more easily than
|
|
|
|
// rebuilding the whole VM image.
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
addMeta("buildlet-binary-url", hconf.BuildletBinaryURL(buildenv.ByProjectID(opts.ProjectID)))
|
|
|
|
addMeta("buildlet-host-type", hostType)
|
2015-01-16 23:59:14 +03:00
|
|
|
if !opts.TLS.IsZero() {
|
|
|
|
addMeta("tls-cert", opts.TLS.CertPEM)
|
|
|
|
addMeta("tls-key", opts.TLS.KeyPEM)
|
|
|
|
addMeta("password", opts.TLS.Password())
|
|
|
|
}
|
2018-05-04 05:42:17 +03:00
|
|
|
if hconf.IsContainer() {
|
|
|
|
addMeta("gce-container-declaration", fmt.Sprintf(`spec:
|
|
|
|
containers:
|
|
|
|
- name: buildlet
|
|
|
|
image: 'gcr.io/%s/%s'
|
|
|
|
volumeMounts:
|
|
|
|
- name: tmpfs-0
|
|
|
|
mountPath: /workdir
|
|
|
|
securityContext:
|
|
|
|
privileged: true
|
|
|
|
stdin: false
|
|
|
|
tty: false
|
|
|
|
restartPolicy: Always
|
|
|
|
volumes:
|
|
|
|
- name: tmpfs-0
|
|
|
|
emptyDir:
|
|
|
|
medium: Memory
|
|
|
|
`, opts.ProjectID, hconf.ContainerImage))
|
|
|
|
}
|
2015-01-16 03:29:16 +03:00
|
|
|
|
2018-05-04 05:42:17 +03:00
|
|
|
if opts.DeleteIn > 0 {
|
2015-01-16 03:29:16 +03:00
|
|
|
// In case the VM gets away from us (generally: if the
|
|
|
|
// coordinator dies while a build is running), then we
|
|
|
|
// set this attribute of when it should be killed so
|
|
|
|
// we can kill it later when the coordinator is
|
|
|
|
// restarted. The cleanUpOldVMs goroutine loop handles
|
|
|
|
// that killing.
|
2015-01-16 23:59:14 +03:00
|
|
|
addMeta("delete-at", fmt.Sprint(time.Now().Add(opts.DeleteIn).Unix()))
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
2015-01-16 23:59:14 +03:00
|
|
|
|
2015-01-16 03:29:16 +03:00
|
|
|
for k, v := range opts.Meta {
|
2015-01-16 23:59:14 +03:00
|
|
|
addMeta(k, v)
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
|
|
|
|
2015-03-21 02:14:52 +03:00
|
|
|
apiGate()
|
2015-01-16 03:29:16 +03:00
|
|
|
op, err := computeService.Instances.Insert(projectID, zone, instance).Do()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to create instance: %v", err)
|
|
|
|
}
|
2015-01-16 20:54:03 +03:00
|
|
|
condRun(opts.OnInstanceRequested)
|
2015-01-16 03:29:16 +03:00
|
|
|
createOp := op.Name
|
|
|
|
|
|
|
|
// Wait for instance create operation to succeed.
|
|
|
|
OpLoop:
|
|
|
|
for {
|
|
|
|
time.Sleep(2 * time.Second)
|
2015-03-21 02:14:52 +03:00
|
|
|
apiGate()
|
2015-01-16 03:29:16 +03:00
|
|
|
op, err := computeService.ZoneOperations.Get(projectID, zone, createOp).Do()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to get op %s: %v", createOp, err)
|
|
|
|
}
|
|
|
|
switch op.Status {
|
|
|
|
case "PENDING", "RUNNING":
|
|
|
|
continue
|
|
|
|
case "DONE":
|
|
|
|
if op.Error != nil {
|
|
|
|
for _, operr := range op.Error.Errors {
|
2015-06-05 04:25:50 +03:00
|
|
|
log.Printf("failed to create instance %s in zone %s: %v", instName, zone, operr.Code)
|
2015-03-22 20:50:04 +03:00
|
|
|
// TODO: catch Code=="QUOTA_EXCEEDED" and "Message" and return
|
|
|
|
// a known error value/type.
|
2015-01-16 03:29:16 +03:00
|
|
|
return nil, fmt.Errorf("Error creating instance: %+v", operr)
|
|
|
|
}
|
|
|
|
return nil, errors.New("Failed to start.")
|
|
|
|
}
|
|
|
|
break OpLoop
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("Unknown create status %q: %+v", op.Status, op)
|
|
|
|
}
|
|
|
|
}
|
2015-01-16 20:54:03 +03:00
|
|
|
condRun(opts.OnInstanceCreated)
|
2015-01-16 03:29:16 +03:00
|
|
|
|
2015-03-21 02:14:52 +03:00
|
|
|
apiGate()
|
2015-01-16 03:29:16 +03:00
|
|
|
inst, err := computeService.Instances.Get(projectID, zone, instName).Do()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error getting instance %s details after creation: %v", instName, err)
|
|
|
|
}
|
|
|
|
|
2015-01-16 23:59:14 +03:00
|
|
|
// Finds its internal and/or external IP addresses.
|
|
|
|
intIP, extIP := instanceIPs(inst)
|
2015-01-16 03:29:16 +03:00
|
|
|
|
|
|
|
// Wait for it to boot and its buildlet to come up.
|
|
|
|
var buildletURL string
|
|
|
|
var ipPort string
|
2015-01-16 23:59:14 +03:00
|
|
|
if !opts.TLS.IsZero() {
|
|
|
|
if extIP == "" {
|
|
|
|
return nil, errors.New("didn't find its external IP address")
|
|
|
|
}
|
|
|
|
buildletURL = "https://" + extIP
|
|
|
|
ipPort = extIP + ":443"
|
2015-01-16 03:29:16 +03:00
|
|
|
} else {
|
2015-01-16 23:59:14 +03:00
|
|
|
if intIP == "" {
|
|
|
|
return nil, errors.New("didn't find its internal IP address")
|
|
|
|
}
|
|
|
|
buildletURL = "http://" + intIP
|
|
|
|
ipPort = intIP + ":80"
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
2015-01-16 20:54:03 +03:00
|
|
|
condRun(opts.OnGotInstanceInfo)
|
2015-01-16 03:29:16 +03:00
|
|
|
|
2017-04-21 22:35:28 +03:00
|
|
|
const timeout = 5 * time.Minute
|
2015-01-16 03:29:16 +03:00
|
|
|
var alive bool
|
|
|
|
impatientClient := &http.Client{
|
|
|
|
Timeout: 5 * time.Second,
|
|
|
|
Transport: &http.Transport{
|
2015-03-04 03:50:12 +03:00
|
|
|
Dial: defaultDialer(),
|
|
|
|
DisableKeepAlives: true,
|
2015-01-16 03:29:16 +03:00
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
deadline := time.Now().Add(timeout)
|
|
|
|
try := 0
|
|
|
|
for time.Now().Before(deadline) {
|
|
|
|
try++
|
2017-07-25 09:55:03 +03:00
|
|
|
if fn := opts.OnBeginBuildletProbe; fn != nil {
|
|
|
|
fn(buildletURL)
|
|
|
|
}
|
2015-01-16 03:29:16 +03:00
|
|
|
res, err := impatientClient.Get(buildletURL)
|
2017-07-25 09:55:03 +03:00
|
|
|
if fn := opts.OnEndBuildletProbe; fn != nil {
|
|
|
|
fn(res, err)
|
|
|
|
}
|
2015-01-16 03:29:16 +03:00
|
|
|
if err != nil {
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
res.Body.Close()
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
return nil, fmt.Errorf("buildlet returned HTTP status code %d on try number %d", res.StatusCode, try)
|
|
|
|
}
|
|
|
|
alive = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !alive {
|
2015-01-28 01:22:21 +03:00
|
|
|
return nil, fmt.Errorf("buildlet didn't come up at %s in %v", buildletURL, timeout)
|
2015-01-16 03:29:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NewClient(ipPort, opts.TLS), nil
|
|
|
|
}
|
2015-01-16 23:59:14 +03:00
|
|
|
|
|
|
|
// DestroyVM sends a request to delete a VM. Actual VM description is
|
|
|
|
// currently (2015-01-19) very slow for no good reason. This function
|
|
|
|
// returns once it's been requested, not when it's done.
|
|
|
|
func DestroyVM(ts oauth2.TokenSource, proj, zone, instance string) error {
|
2017-04-15 20:03:18 +03:00
|
|
|
computeService, _ := compute.New(oauth2.NewClient(context.TODO(), ts))
|
2015-03-21 02:14:52 +03:00
|
|
|
apiGate()
|
2015-01-16 23:59:14 +03:00
|
|
|
_, err := computeService.Instances.Delete(proj, zone, instance).Do()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
type VM struct {
|
|
|
|
// Name is the name of the GCE VM instance.
|
|
|
|
// For example, it's of the form "mote-bradfitz-plan9-386-foo",
|
|
|
|
// and not "plan9-386-foo".
|
|
|
|
Name string
|
|
|
|
IPPort string
|
|
|
|
TLS KeyPair
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
Type string // buildlet type
|
2015-01-16 23:59:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListVMs lists all VMs.
|
|
|
|
func ListVMs(ts oauth2.TokenSource, proj, zone string) ([]VM, error) {
|
|
|
|
var vms []VM
|
2017-04-15 20:03:18 +03:00
|
|
|
computeService, _ := compute.New(oauth2.NewClient(context.TODO(), ts))
|
2015-01-16 23:59:14 +03:00
|
|
|
|
|
|
|
// TODO(bradfitz): paging over results if more than 500
|
2015-03-21 02:14:52 +03:00
|
|
|
apiGate()
|
2015-01-16 23:59:14 +03:00
|
|
|
list, err := computeService.Instances.List(proj, zone).Do()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, inst := range list.Items {
|
|
|
|
if inst.Metadata == nil {
|
|
|
|
// Defensive. Not seen in practice.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
meta := map[string]string{}
|
|
|
|
for _, it := range inst.Metadata.Items {
|
2015-09-03 20:27:24 +03:00
|
|
|
if it.Value != nil {
|
|
|
|
meta[it.Key] = *it.Value
|
|
|
|
}
|
2015-01-16 23:59:14 +03:00
|
|
|
}
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
hostType := meta["buildlet-host-type"]
|
|
|
|
if hostType == "" {
|
2015-01-16 23:59:14 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
vm := VM{
|
|
|
|
Name: inst.Name,
|
all: split builder config into builder & host configs
Our builders are named of the form "GOOS-GOARCH" or
"GOOS-GOARCH-suffix".
Over time we've grown many builders. This CL doesn't change
that. Builders continue to be named and operate as before.
Previously the build configuration file (dashboard/builders.go) made
each builder type ("linux-amd64-race", etc) define how to create a
host running a buildlet of that type, even though many builders had
identical host configs. For example, these builders all share the same
host type (a Kubernetes container):
linux-amd64
linux-amd64-race
linux-386
linux-386-387
And these are the same host type (a GCE VM):
windows-amd64-gce
windows-amd64-race
windows-386-gce
This CL creates a new concept of a "hostType" which defines how
the buildlet is created (Kube, GCE, Reverse, and how), and then each
builder itself references a host type.
Users never see the hostType. (except perhaps in gomote list output)
But they at least never need to care about them.
Reverse buildlets now can only be one hostType at a time, which
simplifies things. We were no longer using multiple roles per machine
once moving to VMs for OS X.
gomote continues to operate as it did previously but its underlying
protocol changed and clients will need to be updated. As a new
feature, gomote now has a new flag to let you reuse a buildlet host
connection for different builder rules if they share the same
underlying host type. But users can ignore that.
This CL is a long-standing TODO (previously attempted and aborted) and
will make many things easier and faster, including the linux-arm
cross-compilation effort, and keeping pre-warmed buildlets of VM types
ready to go.
Updates golang/go#17104
Change-Id: Iad8387f48680424a8441e878a2f4762bf79ea4d2
Reviewed-on: https://go-review.googlesource.com/29551
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-09-22 00:27:37 +03:00
|
|
|
Type: hostType,
|
2015-01-16 23:59:14 +03:00
|
|
|
TLS: KeyPair{
|
|
|
|
CertPEM: meta["tls-cert"],
|
|
|
|
KeyPEM: meta["tls-key"],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
_, extIP := instanceIPs(inst)
|
|
|
|
if extIP == "" || vm.TLS.IsZero() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
vm.IPPort = extIP + ":443"
|
|
|
|
vms = append(vms, vm)
|
|
|
|
}
|
|
|
|
return vms, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func instanceIPs(inst *compute.Instance) (intIP, extIP string) {
|
|
|
|
for _, iface := range inst.NetworkInterfaces {
|
|
|
|
if strings.HasPrefix(iface.NetworkIP, "10.") {
|
|
|
|
intIP = iface.NetworkIP
|
|
|
|
}
|
|
|
|
for _, accessConfig := range iface.AccessConfigs {
|
|
|
|
if accessConfig.Type == "ONE_TO_ONE_NAT" {
|
|
|
|
extIP = accessConfig.NatIP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2018-05-04 05:42:17 +03:00
|
|
|
|
|
|
|
var (
|
|
|
|
cosListMu sync.Mutex
|
|
|
|
cosCachedTime time.Time
|
|
|
|
cosCachedImage string
|
|
|
|
)
|
|
|
|
|
|
|
|
// cosImage returns the GCP VM image name of the latest stable
|
|
|
|
// Container-Optimized OS image. It caches results for 15 minutes.
|
|
|
|
func cosImage(ctx context.Context, svc *compute.Service) (string, error) {
|
|
|
|
const cacheDuration = 15 * time.Minute
|
|
|
|
cosListMu.Lock()
|
|
|
|
defer cosListMu.Unlock()
|
|
|
|
if cosCachedImage != "" && cosCachedTime.After(time.Now().Add(-cacheDuration)) {
|
|
|
|
return cosCachedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
imList, err := svc.Images.List("cos-cloud").Filter(`(family eq "cos-stable")`).Context(ctx).Do()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if imList.NextPageToken != "" {
|
|
|
|
return "", fmt.Errorf("too many images; pagination not supported")
|
|
|
|
}
|
|
|
|
ims := imList.Items
|
|
|
|
if len(ims) == 0 {
|
|
|
|
return "", errors.New("no image found")
|
|
|
|
}
|
|
|
|
sort.Slice(ims, func(i, j int) bool {
|
|
|
|
if ims[i].Deprecated == nil && ims[j].Deprecated != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return ims[i].CreationTimestamp > ims[j].CreationTimestamp
|
|
|
|
})
|
|
|
|
|
|
|
|
im := ims[0].SelfLink
|
|
|
|
cosCachedImage = im
|
|
|
|
cosCachedTime = time.Now()
|
|
|
|
return im, nil
|
|
|
|
}
|