cmd/gomote: implements GRPC ls command

This change adds the implementation for the GRPC ls command to the
gomote client.

Updates golang/go#48737
For golang/go#47521

Change-Id: Ie08f26f119e72de69ce5716a47edd9c38b88af9b
Reviewed-on: https://go-review.googlesource.com/c/build/+/398495
Run-TryBot: Carlos Amedee <carlos@golang.org>
Auto-Submit: Carlos Amedee <carlos@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alex Rakoczy <alex@golang.org>
diff --git a/cmd/gomote/gomote.go b/cmd/gomote/gomote.go
index 7771c85..9290181 100644
--- a/cmd/gomote/gomote.go
+++ b/cmd/gomote/gomote.go
@@ -152,7 +152,7 @@
 	registerCommand("create", "create a buildlet; with no args, list types of buildlets", legacyCreate)
 	registerCommand("destroy", "destroy a buildlet", legacyDestroy)
 	registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
-	registerCommand("ls", "list the contents of a directory on a buildlet", ls)
+	registerCommand("ls", "list the contents of a directory on a buildlet", legacyLs)
 	registerCommand("list", "list active buildlets", legacyList)
 	registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
 	registerCommand("push", "sync your GOROOT directory to the buildlet", push)
@@ -219,6 +219,7 @@
 		"create":  create,
 		"destroy": destroy,
 		"list":    list,
+		"ls":      ls,
 	}
 	if len(args) == 0 {
 		usage()
diff --git a/cmd/gomote/ls.go b/cmd/gomote/ls.go
index fc8f4fe..ef30a4a 100644
--- a/cmd/gomote/ls.go
+++ b/cmd/gomote/ls.go
@@ -12,9 +12,10 @@
 	"strings"
 
 	"golang.org/x/build/buildlet"
+	"golang.org/x/build/internal/gomote/protos"
 )
 
-func ls(args []string) error {
+func legacyLs(args []string) error {
 	fs := flag.NewFlagSet("ls", flag.ContinueOnError)
 	fs.Usage = func() {
 		fmt.Fprintln(os.Stderr, "ls usage: gomote ls <instance> [-R] [dir]")
@@ -49,3 +50,43 @@
 		fmt.Fprintf(os.Stdout, "%s\n", bi)
 	})
 }
+
+func ls(args []string) error {
+	fs := flag.NewFlagSet("ls", flag.ContinueOnError)
+	fs.Usage = func() {
+		fmt.Fprintln(os.Stderr, "ls usage: gomote v2 ls <instance> [-R] [dir]")
+		fs.PrintDefaults()
+		os.Exit(1)
+	}
+	var recursive bool
+	fs.BoolVar(&recursive, "R", false, "recursive")
+	var digest bool
+	fs.BoolVar(&digest, "d", false, "get file digests")
+	var skip string
+	fs.StringVar(&skip, "skip", "", "comma-separated list of relative directories to skip (use forward slashes)")
+	fs.Parse(args)
+
+	dir := "."
+	if n := fs.NArg(); n < 1 || n > 2 {
+		fs.Usage()
+	} else if n == 2 {
+		dir = fs.Arg(1)
+	}
+	name := fs.Arg(0)
+	ctx := context.Background()
+	client := gomoteServerClient(ctx)
+	resp, err := client.ListDirectory(ctx, &protos.ListDirectoryRequest{
+		GomoteId:  name,
+		Directory: dir,
+		Recursive: recursive,
+		SkipFiles: strings.Split(skip, ","),
+		Digest:    digest,
+	})
+	if err != nil {
+		return fmt.Errorf("unable to ls: %s", statusFromError(err))
+	}
+	for _, entry := range resp.GetEntries() {
+		fmt.Fprintf(os.Stdout, "%s\n", entry)
+	}
+	return nil
+}