blob: 6fa00ca19440570d1935f09aca6ff2ff6ab24482 [file] [log] [blame]
// Copyright 2020 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.
//go:build linux || darwin
// +build linux darwin
package pool
import (
"context"
"fmt"
"log"
"math/rand"
"strings"
"time"
"golang.org/x/build/buildlet"
"golang.org/x/build/dashboard"
)
// Buildlet defines an interface for a pool of buildlets.
type Buildlet interface {
// GetBuildlet returns a new buildlet client.
//
// The hostType is the key into the dashboard.Hosts
// map (such as "host-linux-jessie"), NOT the buidler type
// ("linux-386").
//
// Users of GetBuildlet must both call Client.Close when done
// with the client as well as cancel the provided Context.
GetBuildlet(ctx context.Context, hostType string, lg Logger) (buildlet.Client, error)
String() string // TODO(bradfitz): more status stuff
}
// IsRemoteBuildletFunc should report whether the buildlet instance name is
// is a remote buildlet. This is applicable to GCE and EC2 instances.
//
// TODO(golang.org/issue/38337): should be removed once remote buildlet management
// functions are moved into a package.
type IsRemoteBuildletFunc func(instanceName string) bool
// randHex generates a random hex string.
func randHex(n int) string {
buf := make([]byte, n/2+1)
if _, err := rand.Read(buf); err != nil {
log.Fatalf("randHex: %v", err)
}
return fmt.Sprintf("%x", buf)[:n]
}
func friendlyDuration(d time.Duration) string {
if d > 10*time.Second {
d2 := ((d + 50*time.Millisecond) / (100 * time.Millisecond)) * (100 * time.Millisecond)
return d2.String()
}
if d > time.Second {
d2 := ((d + 5*time.Millisecond) / (10 * time.Millisecond)) * (10 * time.Millisecond)
return d2.String()
}
d2 := ((d + 50*time.Microsecond) / (100 * time.Microsecond)) * (100 * time.Microsecond)
return d2.String()
}
// instanceName generates a random instance name according to the host type.
func instanceName(hostType string, length int) string {
return fmt.Sprintf("buildlet-%s-rn%s", strings.TrimPrefix(hostType, "host-"), randHex(length))
}
// determineDeleteTimeout determines the buildlet timeout duration with the
// following priority:
//
// 1. Host type default from host config.
// 2. Global default from 'timeout'.
func determineDeleteTimeout(host *dashboard.HostConfig, timeout time.Duration) time.Duration {
if host.DeleteTimeout != 0 {
return host.DeleteTimeout
}
return timeout
}
// isBuildlet checks the name string in order to determine if the name is for a buildlet.
func isBuildlet(name string) bool {
return strings.HasPrefix(name, "buildlet-")
}
// TestPoolHook is used to override the buildlet returned by ForConf. It should only be used for
// testing purposes.
var TestPoolHook func(*dashboard.HostConfig) Buildlet
// ForHost returns the appropriate buildlet depending on the host configuration that is passed it.
// The returned buildlet can be overriden for testing purposes by registering a test hook.
func ForHost(conf *dashboard.HostConfig) Buildlet {
if TestPoolHook != nil {
return TestPoolHook(conf)
}
if conf == nil {
panic("nil conf")
}
switch {
case conf.IsEC2():
return EC2BuildetPool()
case conf.IsVM():
return NewGCEConfiguration().BuildletPool()
case conf.IsContainer():
if NewGCEConfiguration().BuildEnv().PreferContainersOnCOS || KubeErr() != nil {
return NewGCEConfiguration().BuildletPool() // it also knows how to do containers.
} else {
return KubePool()
}
case conf.IsReverse:
return ReversePool()
default:
panic(fmt.Sprintf("no buildlet pool for host type %q", conf.HostType))
}
}