blob: 8db5f31db3ff5f1376a94af4ab00ce7fd3f1adb8 [file] [log] [blame]
// 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 (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"sort"
"strings"
"time"
"golang.org/x/build/buildlet"
"golang.org/x/build/internal/gomote/protos"
"golang.org/x/build/types"
)
type builderType struct {
Name string
IsReverse bool
ExpectNum int
}
func builders() (bt []builderType) {
type builderInfo struct {
HostType string
}
type hostInfo struct {
IsReverse bool
ExpectNum int
ContainerImage string
VMImage string
}
// resj is the response JSON from the builders.
var resj struct {
Builders map[string]builderInfo
Hosts map[string]hostInfo
}
res, err := http.Get("https://farmer.golang.org/builders?mode=json")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("fetching builder types: %s", res.Status)
}
if err := json.NewDecoder(res.Body).Decode(&resj); err != nil {
log.Fatalf("decoding builder types: %v", err)
}
for b, bi := range resj.Builders {
if strings.HasPrefix(b, "misc-compile") {
continue
}
hi, ok := resj.Hosts[bi.HostType]
if !ok {
continue
}
if !hi.IsReverse && hi.ContainerImage == "" && hi.VMImage == "" {
continue
}
bt = append(bt, builderType{
Name: b,
IsReverse: hi.IsReverse,
ExpectNum: hi.ExpectNum,
})
}
sort.Slice(bt, func(i, j int) bool {
return bt[i].Name < bt[j].Name
})
return
}
func legacyCreate(args []string) error {
fs := flag.NewFlagSet("create", flag.ContinueOnError)
fs.Usage = func() {
fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>")
fs.PrintDefaults()
fmt.Fprintln(os.Stderr, "\nValid types:")
for _, bt := range builders() {
var warn string
if bt.IsReverse {
if bt.ExpectNum > 0 {
warn = fmt.Sprintf(" [limited capacity: %d machines]", bt.ExpectNum)
} else {
warn = " [limited capacity]"
}
}
fmt.Fprintf(os.Stderr, " * %s%s\n", bt.Name, warn)
}
os.Exit(1)
}
var status bool
fs.BoolVar(&status, "status", true, "print regular status updates while waiting")
// 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 {
fs.Usage()
}
builderType := fs.Arg(0)
t := time.Now()
cc, err := buildlet.NewCoordinatorClientFromFlags()
if err != nil {
return fmt.Errorf("failed to create coordinator client: %v", err)
}
client, err := cc.CreateBuildletWithStatus(builderType, func(st types.BuildletWaitStatus) {
if status {
if st.Message != "" {
fmt.Fprintf(os.Stderr, "# %s\n", st.Message)
return
}
fmt.Fprintf(os.Stderr, "# still creating %s after %v; %d requests ahead of you\n", builderType, time.Since(t).Round(time.Second), st.Ahead)
}
})
if err != nil {
return fmt.Errorf("failed to create buildlet: %v", err)
}
fmt.Println(client.RemoteName())
return nil
}
func create(args []string) error {
fs := flag.NewFlagSet("create", flag.ContinueOnError)
fs.Usage = func() {
fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>")
fs.PrintDefaults()
fmt.Fprintln(os.Stderr, "\nValid types:")
for _, bt := range builders() {
var warn string
if bt.IsReverse {
if bt.ExpectNum > 0 {
warn = fmt.Sprintf(" [limited capacity: %d machines]", bt.ExpectNum)
} else {
warn = " [limited capacity]"
}
}
fmt.Fprintf(os.Stderr, " * %s%s\n", bt.Name, warn)
}
os.Exit(1)
}
var status bool
fs.BoolVar(&status, "status", true, "print regular status updates while waiting")
fs.Parse(args)
if fs.NArg() != 1 {
fs.Usage()
}
builderType := fs.Arg(0)
ctx := context.Background()
client := gomoteServerClient(ctx)
start := time.Now()
stream, err := client.CreateInstance(ctx, &protos.CreateInstanceRequest{BuilderType: builderType})
if err != nil {
return fmt.Errorf("failed to create buildlet: %v", statusFromError(err))
}
var instanceName string
for {
update, err := stream.Recv()
switch {
case err == io.EOF:
fmt.Println(instanceName)
return nil
case err != nil:
return fmt.Errorf("failed to create buildlet: %v", statusFromError(err))
case update.GetStatus() != protos.CreateInstanceResponse_COMPLETE && status:
fmt.Fprintf(os.Stderr, "# still creating %s after %v; %d requests ahead of you\n", builderType, time.Since(start).Round(time.Second), update.GetWaitersAhead())
case update.GetStatus() == protos.CreateInstanceResponse_COMPLETE:
instanceName = update.GetInstance().GetGomoteId()
}
}
}