Skip to content

Commit

Permalink
cmd/gomote: remove the old create-VMs-directly model; always use coor…
Browse files Browse the repository at this point in the history
…dinator

Fixes golang/go#11711

Change-Id: I1c05de286393a610b74595f57487ebd341b6e599
Reviewed-on: https://go-review.googlesource.com/12393
Reviewed-by: Andrew Gerrand <adg@golang.org>
  • Loading branch information
bradfitz committed Jul 20, 2015
1 parent 2f8733f commit 6a12af6
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 201 deletions.
44 changes: 2 additions & 42 deletions cmd/gomote/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import (
"path/filepath"
"runtime"
"strings"

"golang.org/x/build/auth"
"golang.org/x/build/buildlet"
"golang.org/x/oauth2"
"google.golang.org/api/compute/v1"
)

func username() string {
Expand All @@ -42,41 +37,6 @@ func configDir() string {
return filepath.Join(homeDir(), ".config", "gomote")
}

func projTokenSource() oauth2.TokenSource {
ts, err := auth.ProjectTokenSource(*proj, compute.ComputeScope)
if err != nil {
log.Fatalf("Failed to get OAuth2 token source for project %s: %v", *proj, err)
}
return ts
}

func userKeyPair() buildlet.KeyPair {
keyDir := configDir()
crtFile := filepath.Join(keyDir, "gomote.crt")
keyFile := filepath.Join(keyDir, "gomote.key")
_, crtErr := os.Stat(crtFile)
_, keyErr := os.Stat(keyFile)
if crtErr == nil && keyErr == nil {
return buildlet.KeyPair{
CertPEM: slurpString(crtFile),
KeyPEM: slurpString(keyFile),
}
}
check := func(what string, err error) {
if err != nil {
log.Printf("%s: %v", what, err)
}
}
check("making key dir", os.MkdirAll(keyDir, 0700))
kp, err := buildlet.NewKeyPair()
if err != nil {
log.Fatalf("Error generating new key pair: %v", err)
}
check("writing cert file: ", ioutil.WriteFile(crtFile, []byte(kp.CertPEM), 0600))
check("writing key file: ", ioutil.WriteFile(keyFile, []byte(kp.KeyPEM), 0600))
return kp
}

func slurpString(f string) string {
slurp, err := ioutil.ReadFile(f)
if err != nil {
Expand All @@ -91,8 +51,8 @@ func userToken() string {
}
keyDir := configDir()
baseFile := "user-" + *user + ".token"
if *dev {
baseFile = "dev-" + baseFile
if *staging {
baseFile = "staging-" + baseFile
}
tokenFile := filepath.Join(keyDir, baseFile)
slurp, err := ioutil.ReadFile(tokenFile)
Expand Down
44 changes: 9 additions & 35 deletions cmd/gomote/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ package main
import (
"flag"
"fmt"
"log"
"os"
"sort"
"strings"
"time"

"golang.org/x/build/buildlet"
"golang.org/x/build/dashboard"
)

Expand All @@ -37,8 +34,10 @@ func create(args []string) error {
}
os.Exit(1)
}
var timeout time.Duration
fs.DurationVar(&timeout, "timeout", 60*time.Minute, "how long the VM will live before being deleted.")
// TODO(bradfitz): restore this option, and send it to the coordinator:
// For now, comment it out so it's not misleading.
// var timeout time.Duration
// fs.DurationVar(&timeout, "timeout", 60*time.Minute, "how long the VM will live before being deleted.")

fs.Parse(args)
if fs.NArg() != 1 {
Expand All @@ -63,36 +62,11 @@ func create(args []string) error {
}
}

if *user == "" {
instPrefix := fmt.Sprintf("mote-%s-", username())
instName, err := nextName(instPrefix + builderType)
if err != nil {
return err
}
client, err := buildlet.StartNewVM(projTokenSource(), instName, builderType, buildlet.VMOpts{
Zone: *zone,
ProjectID: *proj,
TLS: userKeyPair(),
DeleteIn: timeout,
Description: fmt.Sprintf("gomote buildlet for %s", username()),
OnInstanceRequested: func() {
log.Printf("Sent create request. Waiting for operation.")
},
OnInstanceCreated: func() {
log.Printf("Instance created.")
},
})
if err != nil {
return fmt.Errorf("failed to create VM: %v", err)
}
fmt.Printf("%s\t%s\n", strings.TrimPrefix(instName, instPrefix), client.URL())
} else {
cc := coordinatorClient()
client, err := cc.CreateBuildlet(builderType)
if err != nil {
return fmt.Errorf("failed to create buildlet: %v", err)
}
fmt.Println(client.RemoteName())
cc := coordinatorClient()
client, err := cc.CreateBuildlet(builderType)
if err != nil {
return fmt.Errorf("failed to create buildlet: %v", err)
}
fmt.Println(client.RemoteName())
return nil
}
6 changes: 1 addition & 5 deletions cmd/gomote/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,5 @@ func destroy(args []string) error {
if err != nil {
return err
}
if *user == "" {
return bc.DestroyVM(projTokenSource(), *proj, *zone, fmt.Sprintf("mote-%s-%s", username(), name))
} else {
return bc.Destroy()
}
return bc.Destroy()
}
17 changes: 4 additions & 13 deletions cmd/gomote/gomote.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,9 @@ import (
)

var (
user = flag.String("user", username(), "gomote server username; if set to the empty string, only GCE VMs can be created, without using the coordinator.")
user = flag.String("user", username(), "gomote server username. You must have the token for user-$USER. The error message will say where to put it.")

proj = flag.String("project", "symbolic-datum-552", "GCE project owning builders")
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 staging project")
buildletBucket = flag.String("buildletbucket", "", "Optional alternate GCS bucket for the buildlet binaries.")
staging = flag.Bool("staging", false, "if true, use the staging build coordinator and buildlets")
)

type command struct {
Expand Down Expand Up @@ -84,7 +79,7 @@ func registerCommand(name, des string, run func([]string) error) {

func coordinatorClient() *buildlet.CoordinatorClient {
inst := build.ProdCoordinator
if *dev {
if *staging {
inst = build.StagingCoordinator
}
return &buildlet.CoordinatorClient{
Expand Down Expand Up @@ -115,13 +110,9 @@ func main() {
registerCommands()
flag.Usage = usage
flag.Parse()
if *dev {
*proj = "go-dashboard-dev"
if *staging {
dashboard.BuildletBucket = "dev-go-builder-data"
}
if v := *buildletBucket; v != "" {
dashboard.BuildletBucket = v
}
args := flag.Args()
if len(args) == 0 {
usage()
Expand Down
107 changes: 35 additions & 72 deletions cmd/gomote/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,62 +28,51 @@ func list(args []string) error {
fs.Usage()
}

if *user == "" {
prefix := fmt.Sprintf("mote-%s-", username())
vms, err := buildlet.ListVMs(projTokenSource(), *proj, *zone)
if err != nil {
return fmt.Errorf("failed to list VMs: %v", err)
}
for _, vm := range vms {
if !strings.HasPrefix(vm.Name, prefix) {
continue
}
fmt.Printf("%s\thttps://%s\n", strings.TrimPrefix(vm.Name, prefix), strings.TrimSuffix(vm.IPPort, ":443"))
}
} else {
cc := coordinatorClient()
rbs, err := cc.RemoteBuildlets()
if err != nil {
log.Fatal(err)
}
for _, rb := range rbs {
fmt.Printf("%s\t%s\texpires in %v\n", rb.Name, rb.Type, rb.Expires.Sub(time.Now()))
}
cc := coordinatorClient()
rbs, err := cc.RemoteBuildlets()
if err != nil {
log.Fatal(err)
}
for _, rb := range rbs {
fmt.Printf("%s\t%s\texpires in %v\n", rb.Name, rb.Type, rb.Expires.Sub(time.Now()))
}

return nil
}

func namedClient(name string) (*buildlet.Client, error) {
if strings.Contains(name, ":") {
return buildlet.NewClient(name, buildlet.NoKeyPair), nil
}
if *user != "" {
cc := coordinatorClient()
return cc.NamedBuildlet(name)
}
// TODO(bradfitz): cache the list on disk and avoid the API call?
vms, err := buildlet.ListVMs(projTokenSource(), *proj, *zone)
func clientAndConf(name string) (bc *buildlet.Client, conf dashboard.BuildConfig, err error) {
cc := coordinatorClient()

rbs, err := cc.RemoteBuildlets()
if err != nil {
return nil, fmt.Errorf("error listing VMs while looking up %q: %v", name, err)
return
}
wantName := fmt.Sprintf("mote-%s-%s", username(), name)
var matches []buildlet.VM
for _, vm := range vms {
if vm.Name == wantName {
return buildlet.NewClient(vm.IPPort, vm.TLS), nil
}
if strings.HasPrefix(vm.Name, wantName) {
matches = append(matches, vm)
var ok bool
for _, rb := range rbs {
if rb.Name == name {
conf, ok = namedConfig(rb.Type)
if !ok {
err = fmt.Errorf("builder %q exists, but unknown type %q", name, rb.Type)
return
}
break
}
}
if len(matches) == 1 {
vm := matches[0]
return buildlet.NewClient(vm.IPPort, vm.TLS), nil
if !ok {
err = fmt.Errorf("unknown builder %q", name)
return
}
if len(matches) > 1 {
return nil, fmt.Errorf("prefix %q is ambiguous", wantName)

bc, err = namedClient(name)
return
}

func namedClient(name string) (*buildlet.Client, error) {
if strings.Contains(name, ":") {
return buildlet.NewClient(name, buildlet.NoKeyPair), nil
}
return nil, fmt.Errorf("buildlet %q not running", name)
cc := coordinatorClient()
return cc.NamedBuildlet(name)
}

// namedConfig returns the builder configuration that matches the given mote
Expand All @@ -97,29 +86,3 @@ func namedConfig(name string) (dashboard.BuildConfig, bool) {
}
return dashboard.Builders[match], match != ""
}

// nextName returns the next available numbered name or the given buildlet base
// name. For example, if the provided prefix is "linux-amd64" and there's
// already an instance named "linux-amd64", nextName will return
// "linux-amd64-1".
func nextName(prefix string) (string, error) {
vms, err := buildlet.ListVMs(projTokenSource(), *proj, *zone)
if err != nil {
return "", fmt.Errorf("error listing VMs: %v", err)
}
matches := map[string]bool{}
for _, vm := range vms {
if strings.HasPrefix(vm.Name, prefix) {
matches[vm.Name] = true
}
}
if len(matches) == 0 || !matches[prefix] {
return prefix, nil
}
for i := 1; ; i++ {
next := fmt.Sprintf("%v-%v", prefix, i)
if !matches[next] {
return next, nil
}
}
}
10 changes: 3 additions & 7 deletions cmd/gomote/put.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,18 +99,14 @@ func put14(args []string) error {
fs.Usage()
}
name := fs.Arg(0)
conf, ok := namedConfig(name)
if !ok {
return fmt.Errorf("unknown builder %q", name)
bc, conf, err := clientAndConf(name)
if err != nil {
return err
}
if conf.Go14URL == "" {
fmt.Printf("No Go14URL field defined for %q; ignoring. (may be baked into image)\n", name)
return nil
}
bc, err := namedClient(name)
if err != nil {
return err
}
return bc.PutTarFromURL(conf.Go14URL, "go1.4")
}

Expand Down
28 changes: 1 addition & 27 deletions cmd/gomote/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,34 +40,8 @@ func run(args []string) error {
name, cmd := fs.Arg(0), fs.Arg(1)

var conf dashboard.BuildConfig
if *user == "" {
var ok bool
conf, ok = namedConfig(name)
if !ok {
return fmt.Errorf("unknown builder type %q", name)
}
} else {
cc := coordinatorClient()
rbs, err := cc.RemoteBuildlets()
if err != nil {
return err
}
var ok bool
for _, rb := range rbs {
if rb.Name == name {
conf, ok = namedConfig(rb.Type)
if !ok {
return fmt.Errorf("builder %q exists, but unknown type %q", name, rb.Type)
}
break
}
}
if !ok {
return fmt.Errorf("unknown builder %q", name)
}
}

bc, err := namedClient(name)
bc, conf, err := clientAndConf(name)
if err != nil {
return err
}
Expand Down

0 comments on commit 6a12af6

Please sign in to comment.