cmd/coordinator: test sharding, better status, logs

Also gomote updates which came about during the process of developing
and debugging this.

Change-Id: Ia53d674118a6b99bcdda7062d3b7161279b6ad52
Reviewed-on: https://go-review.googlesource.com/10463
Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
Brad Fitzpatrick 2015-05-27 21:51:25 -07:00
Родитель aa078a3723
Коммит 79f3fc0823
14 изменённых файлов: 1116 добавлений и 134 удалений

Просмотреть файл

@ -105,6 +105,8 @@ func (c *Client) URL() string {
return "http://" + strings.TrimSuffix(c.ipPort, ":80") return "http://" + strings.TrimSuffix(c.ipPort, ":80")
} }
func (c *Client) IPPort() string { return c.ipPort }
func (c *Client) do(req *http.Request) (*http.Response, error) { func (c *Client) do(req *http.Request) (*http.Response, error) {
if c.password != "" { if c.password != "" {
req.SetBasicAuth("gomote", c.password) req.SetBasicAuth("gomote", c.password)

Просмотреть файл

@ -232,7 +232,7 @@ OpLoop:
} }
condRun(opts.OnGotInstanceInfo) condRun(opts.OnGotInstanceInfo)
const timeout = 90 * time.Second const timeout = 3 * time.Minute
var alive bool var alive bool
impatientClient := &http.Client{ impatientClient := &http.Client{
Timeout: 5 * time.Second, Timeout: 5 * time.Second,

Просмотреть файл

@ -41,3 +41,7 @@ dev-buildlet.windows-amd64: buildlet.go reverse.go buildlet_windows.go
GOOS=windows GOARCH=amd64 go build -o $@ GOOS=windows GOARCH=amd64 go build -o $@
cat $@ | (cd ../upload && go run upload.go --public dev-go-builder-data/buildlet.windows-amd64) cat $@ | (cd ../upload && go run upload.go --public dev-go-builder-data/buildlet.windows-amd64)
dev-buildlet.plan9-386: buildlet.go reverse.go
GOOS=plan9 GOARCH=386 go build -o $@
cat $@ | (cd ../upload && go run upload.go --public dev-go-builder-data/buildlet.plan9-386)

Просмотреть файл

@ -74,6 +74,11 @@ func main() {
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
log.SetOutput(&plan9LogWriter{w: os.Stderr}) log.SetOutput(&plan9LogWriter{w: os.Stderr})
} }
if runtime.GOOS == "linux" {
if w, err := os.OpenFile("/dev/console", os.O_WRONLY, 0); err == nil {
log.SetOutput(w)
}
}
flag.Parse() flag.Parse()
onGCE := metadata.OnGCE() onGCE := metadata.OnGCE()
@ -93,9 +98,12 @@ func main() {
log.Fatalf("error creating workdir: %v", err) log.Fatalf("error creating workdir: %v", err)
} }
default: default:
dir, err := ioutil.TempDir("", "buildlet-scatch") dir := filepath.Join(os.TempDir(), "workdir")
if err != nil { if err := os.RemoveAll(dir); err != nil { // should be no-op
log.Fatalf("error creating workdir with ioutil.TempDir: %v", err) log.Fatal(err)
}
if err := os.Mkdir(dir, 0755); err != nil {
log.Fatal(err)
} }
*workDir = dir *workDir = dir
} }
@ -632,11 +640,15 @@ func handleExec(w http.ResponseWriter, r *http.Request) {
cmd.Stderr = cmdOutput cmd.Stderr = cmdOutput
cmd.Env = env cmd.Env = env
log.Printf("[%p] Running %s with args %q and env %q in dir %s",
cmd, cmd.Path, cmd.Args, cmd.Env, cmd.Dir)
if debug { if debug {
fmt.Fprintf(cmdOutput, ":: Running %s with args %q and env %q in dir %s\n\n", fmt.Fprintf(cmdOutput, ":: Running %s with args %q and env %q in dir %s\n\n",
cmd.Path, cmd.Args, cmd.Env, cmd.Dir) cmd.Path, cmd.Args, cmd.Env, cmd.Dir)
} }
t0 := time.Now()
err := cmd.Start() err := cmd.Start()
if err == nil { if err == nil {
go func() { go func() {
@ -661,7 +673,7 @@ func handleExec(w http.ResponseWriter, r *http.Request) {
} }
} }
w.Header().Set(hdrProcessState, state) w.Header().Set(hdrProcessState, state)
log.Printf("Run = %s", state) log.Printf("[%p] Run = %s, after %v", cmd, state, time.Since(t0))
} }
// setPathEnv returns a copy of the provided environment with any existing // setPathEnv returns a copy of the provided environment with any existing
@ -679,10 +691,11 @@ func setPathEnv(env, path []string, workDir string) []string {
pathIdx = -1 pathIdx = -1
pathOrig = "" pathOrig = ""
) )
for i, s := range env { for i, s := range env {
if strings.HasPrefix(s, "PATH=") { if isPathEnvPair(s) {
pathIdx = i pathIdx = i
pathOrig = strings.TrimPrefix(s, "PATH=") pathOrig = s[len("PaTh="):] // in whatever case
break break
} }
} }
@ -706,8 +719,7 @@ func setPathEnv(env, path []string, workDir string) []string {
// Put the new PATH in env. // Put the new PATH in env.
env = append([]string{}, env...) env = append([]string{}, env...)
// TODO(adg): check that this works on plan 9 pathEnv := pathEnvVar() + "=" + strings.Join(path, pathSeparator())
pathEnv := "PATH=" + strings.Join(path, string(filepath.ListSeparator))
if pathIdx >= 0 { if pathIdx >= 0 {
env[pathIdx] = pathEnv env[pathIdx] = pathEnv
} else { } else {
@ -717,6 +729,39 @@ func setPathEnv(env, path []string, workDir string) []string {
return env return env
} }
// isPathEnvPair reports whether the key=value pair s represents
// the operating system's path variable.
func isPathEnvPair(s string) bool {
// On Unix it's PATH.
// On Plan 9 it's path.
// On Windows it's pAtH case-insensitive.
if runtime.GOOS == "windows" {
return len(s) >= 5 && strings.EqualFold(s[:5], "PATH=")
}
if runtime.GOOS == "plan9" {
return strings.HasPrefix(s, "path=")
}
return strings.HasPrefix(s, "PATH=")
}
// On Unix it's PATH.
// On Plan 9 it's path.
// On Windows it's pAtH case-insensitive.
func pathEnvVar() string {
if runtime.GOOS == "plan9" {
return "path"
}
return "PATH"
}
func pathSeparator() string {
if runtime.GOOS == "plan9" {
return "\x00"
} else {
return string(filepath.ListSeparator)
}
}
func baseEnv() []string { func baseEnv() []string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return windowsBaseEnv() return windowsBaseEnv()
@ -835,6 +880,7 @@ func handleRemoveAll(w http.ResponseWriter, r *http.Request) {
} }
} }
for _, p := range paths { for _, p := range paths {
log.Printf("Removing %s", p)
p = filepath.Join(*workDir, filepath.FromSlash(p)) p = filepath.Join(*workDir, filepath.FromSlash(p))
if err := os.RemoveAll(p); err != nil { if err := os.RemoveAll(p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

Просмотреть файл

@ -1,5 +1,5 @@
coordinator: builders.go coordinator.go dash.go debug.go gce.go reverse.go status.go watcher.go coordinator: builders.go coordinator.go dash.go debug.go gce.go reverse.go status.go watcher.go
GOOS=linux go build -o coordinator . GOOS=linux go build --ldflags="-X main.Version $$USER-$$(TZ=UTC date +%FT%T)Z" -o coordinator .
# After "make upload", either reboot the machine, or ssh to it and: # After "make upload", either reboot the machine, or ssh to it and:
# sudo systemctl restart gobuild.service # sudo systemctl restart gobuild.service

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,21 @@
// 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 main
import (
"strings"
"testing"
)
func TestPartitionGoTests(t *testing.T) {
var in []string
for name := range fixedTestDuration {
in = append(in, name)
}
sets := partitionGoTests(in)
for i, set := range sets {
t.Logf("set %d = \"-run=^(%s)$\"", i, strings.Join(set, "|"))
}
}

Просмотреть файл

@ -14,6 +14,7 @@ import (
"io" "io"
"log" "log"
"path" "path"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -108,7 +109,7 @@ func initGCE() error {
devCluster = projectID == "go-dashboard-dev" devCluster = projectID == "go-dashboard-dev"
if devCluster { if devCluster {
log.Printf("Running in dev cluster") log.Printf("Running in dev cluster")
gcePool.vmCap = make(chan bool, 1) gcePool.vmCap = make(chan bool, 5)
} }
return nil return nil
@ -151,7 +152,7 @@ type gceBuildletPool struct {
vmCap chan bool vmCap chan bool
mu sync.Mutex mu sync.Mutex
instUsed map[string]bool // GCE VM instance name -> true instUsed map[string]time.Time // GCE VM instance name -> creationTime
} }
func (p *gceBuildletPool) SetEnabled(enabled bool) { func (p *gceBuildletPool) SetEnabled(enabled bool) {
@ -162,9 +163,11 @@ func (p *gceBuildletPool) SetEnabled(enabled bool) {
} }
} }
func (p *gceBuildletPool) GetBuildlet(typ, rev string, el eventTimeLogger) (*buildlet.Client, error) { func (p *gceBuildletPool) GetBuildlet(cancel Cancel, typ, rev string, el eventTimeLogger) (*buildlet.Client, error) {
el.logEventTime("awaiting_gce_quota") el.logEventTime("awaiting_gce_quota")
p.awaitVMCountQuota() if err := p.awaitVMCountQuota(cancel); err != nil {
return nil, err
}
// name is the project-wide unique name of the GCE instance. It can't be longer // name is the project-wide unique name of the GCE instance. It can't be longer
// than 61 bytes. // than 61 bytes.
@ -173,8 +176,7 @@ func (p *gceBuildletPool) GetBuildlet(typ, rev string, el eventTimeLogger) (*bui
var needDelete bool var needDelete bool
el.logEventTime("instance_name=" + instName) el.logEventTime("creating_gce_instance", instName)
el.logEventTime("creating_instance")
log.Printf("Creating GCE VM %q for %s at %s", instName, typ, rev) log.Printf("Creating GCE VM %q for %s at %s", instName, typ, rev)
bc, err := buildlet.StartNewVM(tokenSource, instName, typ, buildlet.VMOpts{ bc, err := buildlet.StartNewVM(tokenSource, instName, typ, buildlet.VMOpts{
ProjectID: projectID, ProjectID: projectID,
@ -191,7 +193,7 @@ func (p *gceBuildletPool) GetBuildlet(typ, rev string, el eventTimeLogger) (*bui
needDelete = true // redundant with OnInstanceRequested one, but fine. needDelete = true // redundant with OnInstanceRequested one, but fine.
}, },
OnGotInstanceInfo: func() { OnGotInstanceInfo: func() {
el.logEventTime("waiting_for_buildlet") el.logEventTime("got_instance_info", "waiting_for_buildlet...")
}, },
}) })
if err != nil { if err != nil {
@ -215,23 +217,44 @@ func (p *gceBuildletPool) GetBuildlet(typ, rev string, el eventTimeLogger) (*bui
func (p *gceBuildletPool) WriteHTMLStatus(w io.Writer) { func (p *gceBuildletPool) WriteHTMLStatus(w io.Writer) {
fmt.Fprintf(w, "<b>GCE pool</b> capacity: %d/%d", len(p.vmCap), cap(p.vmCap)) fmt.Fprintf(w, "<b>GCE pool</b> capacity: %d/%d", len(p.vmCap), cap(p.vmCap))
const show = 6 // must be even
active := p.instancesActive()
if len(active) > 0 {
fmt.Fprintf(w, "<ul>")
for i, inst := range active {
if i < show/2 || i >= len(active)-(show/2) {
fmt.Fprintf(w, "<li>%v, %v</li>\n", inst.name, time.Since(inst.creation))
} else if i == show/2 {
fmt.Fprintf(w, "<li>... %d of %d total omitted ...</li>\n", len(active)-show, len(active))
}
}
fmt.Fprintf(w, "</ul>")
}
} }
func (p *gceBuildletPool) String() string { func (p *gceBuildletPool) String() string {
return fmt.Sprintf("GCE pool capacity: %d/%d", len(p.vmCap), cap(p.vmCap)) return fmt.Sprintf("GCE pool capacity: %d/%d", len(p.vmCap), cap(p.vmCap))
} }
func (p *gceBuildletPool) awaitVMCountQuota() { p.vmCap <- true } // awaitVMCountQuota waits for quota.
func (p *gceBuildletPool) awaitVMCountQuota(cancel Cancel) error {
select {
case p.vmCap <- true:
return nil
case <-cancel:
return ErrCanceled
}
}
func (p *gceBuildletPool) putVMCountQuota() { <-p.vmCap } func (p *gceBuildletPool) putVMCountQuota() { <-p.vmCap }
func (p *gceBuildletPool) setInstanceUsed(instName string, used bool) { func (p *gceBuildletPool) setInstanceUsed(instName string, used bool) {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
if p.instUsed == nil { if p.instUsed == nil {
p.instUsed = make(map[string]bool) p.instUsed = make(map[string]time.Time)
} }
if used { if used {
p.instUsed[instName] = true p.instUsed[instName] = time.Now()
} else { } else {
delete(p.instUsed, instName) delete(p.instUsed, instName)
} }
@ -240,9 +263,35 @@ func (p *gceBuildletPool) setInstanceUsed(instName string, used bool) {
func (p *gceBuildletPool) instanceUsed(instName string) bool { func (p *gceBuildletPool) instanceUsed(instName string) bool {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
return p.instUsed[instName] _, ok := p.instUsed[instName]
return ok
} }
func (p *gceBuildletPool) instancesActive() (ret []instanceTime) {
p.mu.Lock()
defer p.mu.Unlock()
for name, create := range p.instUsed {
ret = append(ret, instanceTime{
name: name,
creation: create,
})
}
sort.Sort(byCreationTime(ret))
return ret
}
// instanceTime is a GCE instance name and its creation time.
type instanceTime struct {
name string
creation time.Time
}
type byCreationTime []instanceTime
func (s byCreationTime) Len() int { return len(s) }
func (s byCreationTime) Less(i, j int) bool { return s[i].creation.Before(s[j].creation) }
func (s byCreationTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// cleanUpOldVMs loops forever and periodically enumerates virtual // cleanUpOldVMs loops forever and periodically enumerates virtual
// machines and deletes those which have expired. // machines and deletes those which have expired.
// //

Просмотреть файл

@ -159,7 +159,7 @@ func (p *reverseBuildletPool) reverseHealthCheck() {
p.mu.Unlock() p.mu.Unlock()
} }
func (p *reverseBuildletPool) GetBuildlet(machineType, rev string, el eventTimeLogger) (*buildlet.Client, error) { func (p *reverseBuildletPool) GetBuildlet(cancel Cancel, machineType, rev string, el eventTimeLogger) (*buildlet.Client, error) {
seenErrInUse := false seenErrInUse := false
for { for {
b, err := p.tryToGrab(machineType) b, err := p.tryToGrab(machineType)
@ -175,6 +175,8 @@ func (p *reverseBuildletPool) GetBuildlet(machineType, rev string, el eventTimeL
// a best effort signal. So periodically try to grab // a best effort signal. So periodically try to grab
// a buildlet again. // a buildlet again.
case <-time.After(30 * time.Second): case <-time.After(30 * time.Second):
case <-cancel:
return nil, ErrCanceled
} }
} else if err != nil { } else if err != nil {
return nil, err return nil, err

Просмотреть файл

@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build BROKEN
// I get:
// $ go test -v
// ./reverse_test.go:15: can't find import: "golang.org/x/build/cmd/buildlet"
package main package main
import ( import (

Просмотреть файл

@ -27,6 +27,7 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
Uptime: round(time.Now().Sub(processStartTime)), Uptime: round(time.Now().Sub(processStartTime)),
Recent: append([]*buildStatus{}, statusDone...), Recent: append([]*buildStatus{}, statusDone...),
DiskFree: df, DiskFree: df,
Version: Version,
} }
for _, st := range status { for _, st := range status {
data.Active = append(data.Active, st) data.Active = append(data.Active, st)
@ -84,6 +85,7 @@ type statusData struct {
GCEPoolStatus template.HTML // TODO: embed template GCEPoolStatus template.HTML // TODO: embed template
ReversePoolStatus template.HTML // TODO: embed template ReversePoolStatus template.HTML // TODO: embed template
DiskFree string DiskFree string
Version string
} }
var statusTmpl = template.Must(template.New("status").Parse(` var statusTmpl = template.Must(template.New("status").Parse(`
@ -101,7 +103,7 @@ var statusTmpl = template.Must(template.New("status").Parse(`
</header> </header>
<h2>Running</h2> <h2>Running</h2>
<p>{{printf "%d" .Total}} total builds active. Uptime {{printf "%s" .Uptime}}. <p>{{printf "%d" .Total}} total builds active. Uptime {{printf "%s" .Uptime}}. Version {{.Version}}.
<ul> <ul>
{{range .Active}} {{range .Active}}

Просмотреть файл

@ -24,11 +24,17 @@ import (
"fmt" "fmt"
"os" "os"
"sort" "sort"
"golang.org/x/build/dashboard"
) )
var ( var (
proj = flag.String("project", "symbolic-datum-552", "GCE project owning builders") proj = flag.String("project", "symbolic-datum-552", "GCE project owning builders")
zone = flag.String("zone", "us-central1-a", "GCE zone") zone = flag.String("zone", "us-central1-a", "GCE zone")
// Mostly dev options:
dev = flag.Bool("dev", false, "if true, the default project becomes the default dev project")
buildletBucket = flag.String("buildletbucket", "", "Optional alternate GCS bucket for the buildlet binaries.")
) )
type command struct { type command struct {
@ -91,6 +97,13 @@ func main() {
registerCommands() registerCommands()
flag.Usage = usage flag.Usage = usage
flag.Parse() flag.Parse()
if *dev {
*proj = "go-dashboard-dev"
dashboard.BuildletBucket = "dev-go-builder-data"
}
if v := *buildletBucket; v != "" {
dashboard.BuildletBucket = v
}
args := flag.Args() args := flag.Args()
if len(args) == 0 { if len(args) == 0 {
usage() usage()

Просмотреть файл

@ -27,6 +27,10 @@ func run(args []string) error {
fs.BoolVar(&debug, "debug", false, "write debug info about the command's execution before it begins") fs.BoolVar(&debug, "debug", false, "write debug info about the command's execution before it begins")
var env stringSlice var env stringSlice
fs.Var(&env, "e", "Environment variable KEY=value. The -e flag may be repeated multiple times to add multiple things to the environment.") fs.Var(&env, "e", "Environment variable KEY=value. The -e flag may be repeated multiple times to add multiple things to the environment.")
var path string
fs.StringVar(&path, "path", "", "Comma-separated list of ExecOpts.Path elements. The special string 'EMPTY' means to run without any $PATH. The empty string (default) does not modify the $PATH.")
var dir string
fs.StringVar(&dir, "dir", "", "Directory to run from. Defaults to the directory of the command, or the work directory if -system is true.")
fs.Parse(args) fs.Parse(args)
if fs.NArg() < 2 { if fs.NArg() < 2 {
@ -44,12 +48,21 @@ func run(args []string) error {
return err return err
} }
var pathOpt []string
if path == "EMPTY" {
pathOpt = []string{} // non-nil
} else if path != "" {
pathOpt = strings.Split(path, ",")
}
remoteErr, execErr := bc.Exec(cmd, buildlet.ExecOpts{ remoteErr, execErr := bc.Exec(cmd, buildlet.ExecOpts{
Dir: dir,
SystemLevel: sys || strings.HasPrefix(cmd, "/"), SystemLevel: sys || strings.HasPrefix(cmd, "/"),
Output: os.Stdout, Output: os.Stdout,
Args: fs.Args()[2:], Args: fs.Args()[2:],
ExtraEnv: envutil.Dedup(conf.GOOS() == "windows", append(conf.Env(), []string(env)...)), ExtraEnv: envutil.Dedup(conf.GOOS() == "windows", append(conf.Env(), []string(env)...)),
Debug: debug, Debug: debug,
Path: pathOpt,
}) })
if execErr != nil { if execErr != nil {
return fmt.Errorf("Error trying to execute %s: %v", cmd, execErr) return fmt.Errorf("Error trying to execute %s: %v", cmd, execErr)

Просмотреть файл

@ -30,6 +30,10 @@ type BuildConfig struct {
RegularDisk bool // if true, use spinning disk instead of SSD RegularDisk bool // if true, use spinning disk instead of SSD
TryOnly bool // only used for trybots, and not regular builds TryOnly bool // only used for trybots, and not regular builds
// NumTestHelpers is the number of _additional_ buildlets
// past the first help out with sharded tests.
NumTestHelpers int
// BuildletType optionally specifies the type of buildlet to // BuildletType optionally specifies the type of buildlet to
// request from the buildlet pool. If empty, it defaults to // request from the buildlet pool. If empty, it defaults to
// the value of Name. // the value of Name.
@ -59,12 +63,37 @@ func (c *BuildConfig) GOARCH() string {
return arch[:i] return arch[:i]
} }
// FilePathJoin is mostly like filepath.Join (without the cleaning) except
// it uses the path separator of c.GOOS instead of the host system's.
func (c *BuildConfig) FilePathJoin(x ...string) string {
if c.GOOS() == "windows" {
return strings.Join(x, "\\")
}
return strings.Join(x, "/")
}
// BuildletBucket is the GCS storage bucket which holds the buildlet binaries.
// Tools working in the dev project may change this.
var BuildletBucket = "go-builder-data"
func fixBuildletBucket(u string) string {
if BuildletBucket == "go-builder-data" {
// Prod. Default case.
return u
}
// Dev project remapping:
return strings.Replace(u,
"//storage.googleapis.com/go-builder-data/",
"//storage.googleapis.com/"+BuildletBucket+"/",
1)
}
// BuildletBinaryURL returns the public URL of this builder's buildlet. // BuildletBinaryURL returns the public URL of this builder's buildlet.
func (c *BuildConfig) BuildletBinaryURL() string { func (c *BuildConfig) BuildletBinaryURL() string {
if c.buildletURL != "" { if c.buildletURL != "" {
return c.buildletURL return fixBuildletBucket(c.buildletURL)
} }
return "http://storage.googleapis.com/go-builder-data/buildlet." + c.GOOS() + "-" + c.GOARCH() return fixBuildletBucket("http://storage.googleapis.com/go-builder-data/buildlet." + c.GOOS() + "-" + c.GOARCH())
} }
// SetBuildletBinaryURL sets the public URL of this builder's buildlet. // SetBuildletBinaryURL sets the public URL of this builder's buildlet.
@ -194,6 +223,7 @@ func init() {
VMImage: "freebsd-amd64-gce93", VMImage: "freebsd-amd64-gce93",
machineType: "n1-highcpu-2", machineType: "n1-highcpu-2",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "freebsd-amd64-gce101", Name: "freebsd-amd64-gce101",
@ -202,6 +232,7 @@ func init() {
machineType: "n1-highcpu-2", machineType: "n1-highcpu-2",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-freebsd-amd64.tar.gz",
env: []string{"CC=clang"}, env: []string{"CC=clang"},
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "freebsd-amd64-race", Name: "freebsd-amd64-race",
@ -229,6 +260,7 @@ func init() {
VMImage: "linux-buildlet-std", VMImage: "linux-buildlet-std",
buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64", buildletURL: "http://storage.googleapis.com/go-builder-data/buildlet.linux-amd64",
env: []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386"}, env: []string{"GOROOT_BOOTSTRAP=/go1.4", "GOARCH=386", "GOHOSTARCH=386"},
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "linux-386-387", Name: "linux-386-387",
@ -241,6 +273,7 @@ func init() {
Name: "linux-amd64", Name: "linux-amd64",
VMImage: "linux-buildlet-std", VMImage: "linux-buildlet-std",
env: []string{"GOROOT_BOOTSTRAP=/go1.4"}, env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "all-compile", Name: "all-compile",
@ -275,6 +308,7 @@ func init() {
VMImage: "linux-buildlet-std", VMImage: "linux-buildlet-std",
machineType: "n1-highcpu-4", machineType: "n1-highcpu-4",
env: []string{"GOROOT_BOOTSTRAP=/go1.4"}, env: []string{"GOROOT_BOOTSTRAP=/go1.4"},
// TODO(bradfitz): make race.bash shardable, then: NumTestHelpers: 3
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "linux-386-clang", Name: "linux-386-clang",
@ -399,6 +433,7 @@ func init() {
VMImage: "openbsd-amd64-56", VMImage: "openbsd-amd64-56",
machineType: "n1-highcpu-2", machineType: "n1-highcpu-2",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-amd64.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-amd64.tar.gz",
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "openbsd-386-gce56", Name: "openbsd-386-gce56",
@ -406,16 +441,13 @@ func init() {
VMImage: "openbsd-386-56", VMImage: "openbsd-386-56",
machineType: "n1-highcpu-2", machineType: "n1-highcpu-2",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-386.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-openbsd-386.tar.gz",
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "plan9-386-gcepartial", Name: "plan9-386",
Notes: "Plan 9 from 0intro; GCE VM is built from script in build/env/plan9-386; runs with GOTESTONLY=std (only stdlib tests)", Notes: "Plan 9 from 0intro; GCE VM is built from script in build/env/plan9-386",
VMImage: "plan9-386-v2", VMImage: "plan9-386-v2",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-plan9-386.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-plan9-386.tar.gz",
// It's named "partial" because the buildlet only runs
// the standard library tests ("go test std cmd", basically).
// TODO: run a full Plan 9 builder, or a sharded one.
env: []string{"GOTESTONLY=^go_test:"},
// We *were* using n1-standard-1 because Plan 9 can only // We *were* using n1-standard-1 because Plan 9 can only
// reliably use a single CPU. Using 2 or 4 and we see // reliably use a single CPU. Using 2 or 4 and we see
@ -439,11 +471,9 @@ func init() {
// single-core Plan 9. It will see 2 virtual cores and // single-core Plan 9. It will see 2 virtual cores and
// only use 1, but we hope that 1 will be more powerful // only use 1, but we hope that 1 will be more powerful
// and we'll stop timing out on tests. // and we'll stop timing out on tests.
// machineType: "n1-highcpu-2",
// But then with the toolchain conversion to Go and
// using ramfs, it turns out we need more memory NumTestHelpers: 5, // slow
// anyway, so use n1-highcpu-4.
machineType: "n1-highcpu-4",
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "windows-amd64-gce", Name: "windows-amd64-gce",
@ -452,6 +482,7 @@ func init() {
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-windows-amd64.tar.gz",
RegularDisk: true, RegularDisk: true,
env: []string{"GOARCH=amd64", "GOHOSTARCH=amd64"}, env: []string{"GOARCH=amd64", "GOHOSTARCH=amd64"},
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "windows-amd64-race", Name: "windows-amd64-race",
@ -470,12 +501,14 @@ func init() {
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-windows-386.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-windows-386.tar.gz",
RegularDisk: true, RegularDisk: true,
env: []string{"GOARCH=386", "GOHOSTARCH=386"}, env: []string{"GOARCH=386", "GOHOSTARCH=386"},
NumTestHelpers: 3,
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "darwin-amd64-10_10", Name: "darwin-amd64-10_10",
Notes: "Mac Mini running OS X 10.10 (Yosemite)", Notes: "Mac Mini running OS X 10.10 (Yosemite)",
Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-darwin-amd64.tar.gz", Go14URL: "https://storage.googleapis.com/go-builder-data/go1.4-darwin-amd64.tar.gz",
IsReverse: true, IsReverse: true,
NumTestHelpers: 1, // limited resources
}) })
addBuilder(BuildConfig{ addBuilder(BuildConfig{
Name: "darwin-arm-a5ios", Name: "darwin-arm-a5ios",