Skip to content

Commit 7c4023b

Browse files
committed
cmd/gomote: implements GRPC put command
This change adds the implementation for GRPC put command to the gomote client. Updates golang/go#48737 For golang/go#47521 Change-Id: Ib2376444321ef9d0a754b60bcd3783f66a932f3d Reviewed-on: https://go-review.googlesource.com/c/build/+/406015 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent 96df98a commit 7c4023b

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

cmd/gomote/gomote.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func registerCommands() {
156156
registerCommand("list", "list active buildlets", legacyList)
157157
registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
158158
registerCommand("push", "sync your GOROOT directory to the buildlet", push)
159-
registerCommand("put", "put files on a buildlet", put)
159+
registerCommand("put", "put files on a buildlet", legacyPut)
160160
registerCommand("put14", "put Go 1.4 in place", put14)
161161
registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
162162
registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
@@ -225,6 +225,7 @@ func version2(args []string) error {
225225
"ssh": ssh,
226226
"rm": rm,
227227
"gettar": getTar,
228+
"put": put,
228229
}
229230
if len(args) == 0 {
230231
usage()

cmd/gomote/put.go

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@ package main
66

77
import (
88
"archive/tar"
9+
"bytes"
910
"context"
1011
"errors"
1112
"flag"
1213
"fmt"
1314
"io"
15+
"mime/multipart"
16+
"net/http"
1417
"os"
1518
"path/filepath"
1619
"strconv"
1720
"strings"
1821

22+
"golang.org/x/build/internal/gomote/protos"
1923
"golang.org/x/build/tarutil"
2024
)
2125

@@ -115,8 +119,8 @@ func put14(args []string) error {
115119
return bc.PutTarFromURL(ctx, u, "go1.4")
116120
}
117121

118-
// put single file
119-
func put(args []string) error {
122+
// legacyPut single file
123+
func legacyPut(args []string) error {
120124
fs := flag.NewFlagSet("put", flag.ContinueOnError)
121125
fs.Usage = func() {
122126
fmt.Fprintln(os.Stderr, "put usage: gomote put [put-opts] <buildlet-name> <source or '-' for stdin> [destination]")
@@ -176,3 +180,107 @@ func put(args []string) error {
176180
ctx := context.Background()
177181
return bc.Put(ctx, r, dest, mode)
178182
}
183+
184+
// put single file
185+
func put(args []string) error {
186+
fs := flag.NewFlagSet("put", flag.ContinueOnError)
187+
fs.Usage = func() {
188+
fmt.Fprintln(os.Stderr, "put usage: gomote put [put-opts] <buildlet-name> <source or '-' for stdin> [destination]")
189+
fs.PrintDefaults()
190+
os.Exit(1)
191+
}
192+
modeStr := fs.String("mode", "", "Unix file mode (octal); default to source file mode")
193+
fs.Parse(args)
194+
if n := fs.NArg(); n < 2 || n > 3 {
195+
fs.Usage()
196+
}
197+
198+
var r io.Reader = os.Stdin
199+
var mode os.FileMode = 0666
200+
201+
src := fs.Arg(1)
202+
if src != "-" {
203+
f, err := os.Open(src)
204+
if err != nil {
205+
return err
206+
}
207+
defer f.Close()
208+
r = f
209+
210+
if *modeStr == "" {
211+
fi, err := f.Stat()
212+
if err != nil {
213+
return err
214+
}
215+
mode = fi.Mode()
216+
}
217+
}
218+
if *modeStr != "" {
219+
modeInt, err := strconv.ParseInt(*modeStr, 8, 64)
220+
if err != nil {
221+
return err
222+
}
223+
mode = os.FileMode(modeInt)
224+
if !mode.IsRegular() {
225+
return fmt.Errorf("bad mode: %v", mode)
226+
}
227+
}
228+
dest := fs.Arg(2)
229+
if dest == "" {
230+
if src == "-" {
231+
return errors.New("must specify destination file name when source is standard input")
232+
}
233+
dest = filepath.Base(src)
234+
}
235+
ctx := context.Background()
236+
client := gomoteServerClient(ctx)
237+
resp, err := client.UploadFile(ctx, &protos.UploadFileRequest{})
238+
if err != nil {
239+
return fmt.Errorf("unable to request credentials for a file upload: %s", statusFromError(err))
240+
}
241+
err = uploadToGCS(ctx, resp.GetFields(), r, dest, resp.GetUrl())
242+
if err != nil {
243+
return fmt.Errorf("unable to upload file to GCS: %s", err)
244+
}
245+
name := fs.Arg(0)
246+
_, err = client.WriteFileFromURL(ctx, &protos.WriteFileFromURLRequest{
247+
GomoteId: name,
248+
Url: fmt.Sprintf("%s%s", resp.GetUrl(), resp.GetObjectName()),
249+
Filename: dest,
250+
Mode: uint32(mode),
251+
})
252+
if err != nil {
253+
return fmt.Errorf("unable to write the file from URL: %s", statusFromError(err))
254+
}
255+
return nil
256+
}
257+
258+
func uploadToGCS(ctx context.Context, fields map[string]string, file io.Reader, filename, url string) error {
259+
buf := new(bytes.Buffer)
260+
mw := multipart.NewWriter(buf)
261+
262+
for k, v := range fields {
263+
if err := mw.WriteField(k, v); err != nil {
264+
return fmt.Errorf("unable to write field: %s", err)
265+
}
266+
}
267+
_, err := mw.CreateFormFile("file", filename)
268+
if err != nil {
269+
return fmt.Errorf("unable to create form file: %s", err)
270+
}
271+
// Write our own boundary to avoid buffering entire file into the multipart Writer
272+
bound := fmt.Sprintf("\r\n--%s--\r\n", mw.Boundary())
273+
req, err := http.NewRequestWithContext(ctx, "POST", url, io.NopCloser(io.MultiReader(buf, file, strings.NewReader(bound))))
274+
if err != nil {
275+
return fmt.Errorf("unable to create request: %s", err)
276+
}
277+
req.Header.Set("Content-Type", mw.FormDataContentType())
278+
res, err := http.DefaultClient.Do(req)
279+
if err != nil {
280+
return fmt.Errorf("http request failed: %s", err)
281+
}
282+
if res.StatusCode != http.StatusNoContent {
283+
return fmt.Errorf("http post failed: status code=%d", res.StatusCode)
284+
}
285+
return nil
286+
}

0 commit comments

Comments
 (0)