cmd/gomote: implements GRPC create command
This change adds the implementation for the the GRPC create command to
the gomote client.
Updates golang/go#48737
For golang/go#47521
Change-Id: I2a45364f6aac078c5356f60ddccb9e301985c9c1
Reviewed-on: https://go-review.googlesource.com/c/build/+/398054
Reviewed-by: Alex Rakoczy <alex@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Auto-Submit: Carlos Amedee <carlos@golang.org>
Run-TryBot: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/gomote/create.go b/cmd/gomote/create.go
index 18d0d2c..cccf1e4 100644
--- a/cmd/gomote/create.go
+++ b/cmd/gomote/create.go
@@ -5,9 +5,11 @@
package main
import (
+ "context"
"encoding/json"
"flag"
"fmt"
+ "io"
"log"
"net/http"
"os"
@@ -16,6 +18,7 @@
"time"
"golang.org/x/build/buildlet"
+ "golang.org/x/build/internal/gomote/protos"
"golang.org/x/build/types"
)
@@ -74,7 +77,7 @@
return
}
-func create(args []string) error {
+func legacyCreate(args []string) error {
fs := flag.NewFlagSet("create", flag.ContinueOnError)
fs.Usage = func() {
@@ -128,3 +131,56 @@
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 v2 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()
+ }
+ }
+}
diff --git a/cmd/gomote/gomote.go b/cmd/gomote/gomote.go
index 8c6f404..d55bc6e 100644
--- a/cmd/gomote/gomote.go
+++ b/cmd/gomote/gomote.go
@@ -37,6 +37,7 @@
rdp RDP (Remote Desktop Protocol) to a Windows buildlet
run run a command on a buildlet
ssh ssh to a buildlet
+ v2 version 2 of the gomote API
To list all the builder types available, run "create" with no arguments:
@@ -99,6 +100,7 @@
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
+ "google.golang.org/grpc/status"
)
var (
@@ -147,7 +149,7 @@
}
func registerCommands() {
- registerCommand("create", "create a buildlet; with no args, list types of buildlets", create)
+ registerCommand("create", "create a buildlet; with no args, list types of buildlets", legacyCreate)
registerCommand("destroy", "destroy a buildlet", destroy)
registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
registerCommand("ls", "list the contents of a directory on a buildlet", ls)
@@ -161,6 +163,7 @@
registerCommand("rm", "delete files or directories", rm)
registerCommand("run", "run a command on a buildlet", run)
registerCommand("ssh", "ssh to a buildlet", ssh)
+ registerCommand("v2", "version 2 of the gomote commands", version2)
}
var (
@@ -185,8 +188,7 @@
}
err := cmd.run(args[1:])
if err != nil {
- fmt.Fprintf(os.Stderr, "Error running %s: %v\n", cmdName, err)
- os.Exit(1)
+ logAndExitf("Error running %s: %v\n", cmdName, err)
}
}
@@ -209,8 +211,31 @@
return protos.NewGomoteServiceClient(grpcClient)
}
+type subCommand func([]string) error
+
+// version2 manages how version 2 subcommands are called.
+func version2(args []string) error {
+ cm := map[string]subCommand{
+ "create": create,
+ }
+ if len(args) == 0 {
+ usage()
+ }
+ subCmd := args[0]
+ sc, ok := cm[subCmd]
+ if !ok {
+ return fmt.Errorf("unknown sub-command %q\n", subCmd)
+ }
+ return sc(args[1:])
+}
+
// logAndExitf is equivalent to Printf to Stderr followed by a call to os.Exit(1).
func logAndExitf(format string, v ...interface{}) {
fmt.Fprintf(os.Stderr, format, v...)
os.Exit(1)
}
+
+// statusFromError returns the message portion of a GRPC error.
+func statusFromError(err error) string {
+ return status.Convert(err).Message()
+}