cmd/gomote: get list of buildlet types from server

So users don't need to update their binaries all the time.

Fixes golang/go#30929

Change-Id: I53a6a092578ec0bcee3e47a5dcbbb5c0b9ee9113
Reviewed-on: https://go-review.googlesource.com/c/build/+/169678
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/gomote/create.go b/cmd/gomote/create.go
index 73fdbd5..2ec66db 100644
--- a/cmd/gomote/create.go
+++ b/cmd/gomote/create.go
@@ -5,21 +5,70 @@
 package main
 
 import (
+	"encoding/json"
 	"flag"
 	"fmt"
+	"log"
+	"net/http"
 	"os"
 	"sort"
 	"strings"
 
 	"golang.org/x/build/buildlet"
-	"golang.org/x/build/dashboard"
 )
 
-func vmTypes() (s []string) {
-	for k := range dashboard.Builders {
-		s = append(s, k)
+type builderType struct {
+	Name      string
+	IsReverse bool
+	ExpectNum int
+}
+
+func builders() (bt []builderType) {
+	type builderInfo struct {
+		HostType string
 	}
-	sort.Strings(s)
+	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
 }
 
@@ -30,8 +79,16 @@
 		fmt.Fprintln(os.Stderr, "create usage: gomote create [create-opts] <type>")
 		fs.PrintDefaults()
 		fmt.Fprintln(os.Stderr, "\nValid types:")
-		for _, t := range vmTypes() {
-			fmt.Fprintf(os.Stderr, "  * %s\n", t)
+		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)
 	}
@@ -45,23 +102,6 @@
 		fs.Usage()
 	}
 	builderType := fs.Arg(0)
-	_, ok := dashboard.Builders[builderType]
-	if !ok {
-		var valid []string
-		var prefixMatch []string
-		for k := range dashboard.Builders {
-			valid = append(valid, k)
-			if strings.HasPrefix(k, builderType) {
-				prefixMatch = append(prefixMatch, k)
-			}
-		}
-		if len(prefixMatch) == 1 {
-			builderType = prefixMatch[0]
-		} else {
-			sort.Strings(valid)
-			return fmt.Errorf("Invalid builder type %q. Valid options include: %q", builderType, valid)
-		}
-	}
 
 	cc, err := buildlet.NewCoordinatorClientFromFlags()
 	if err != nil {