@@ -6,16 +6,20 @@ package main
6
6
7
7
import (
8
8
"archive/tar"
9
+ "bytes"
9
10
"context"
10
11
"errors"
11
12
"flag"
12
13
"fmt"
13
14
"io"
15
+ "mime/multipart"
16
+ "net/http"
14
17
"os"
15
18
"path/filepath"
16
19
"strconv"
17
20
"strings"
18
21
22
+ "golang.org/x/build/internal/gomote/protos"
19
23
"golang.org/x/build/tarutil"
20
24
)
21
25
@@ -115,8 +119,8 @@ func put14(args []string) error {
115
119
return bc .PutTarFromURL (ctx , u , "go1.4" )
116
120
}
117
121
118
- // put single file
119
- func put (args []string ) error {
122
+ // legacyPut single file
123
+ func legacyPut (args []string ) error {
120
124
fs := flag .NewFlagSet ("put" , flag .ContinueOnError )
121
125
fs .Usage = func () {
122
126
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 {
176
180
ctx := context .Background ()
177
181
return bc .Put (ctx , r , dest , mode )
178
182
}
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