| 499 | } |
| 500 | |
| 501 | func (runCtx *runContext) uploadFile(ctx context.Context, endpoint string, file string) error { |
| 502 | body := new(bytes.Buffer) |
| 503 | name := filepath.Base(file) |
| 504 | multipartFile := multipart.NewWriter(body) |
| 505 | writer, err := multipartFile.CreateFormFile("file", name) |
| 506 | if err != nil { |
| 507 | return errors.Wrap(ctx, err, "create form file") |
| 508 | } |
| 509 | |
| 510 | fd, err := os.OpenFile(file, os.O_RDONLY, 0600) |
| 511 | if err != nil { |
| 512 | return errors.Wrap(ctx, err, "open file") |
| 513 | } |
| 514 | |
| 515 | _, err = stdio.Copy(writer, fd) |
| 516 | if err != nil { |
| 517 | return errors.Wrap(ctx, err, "copy to form writer") |
| 518 | } |
| 519 | |
| 520 | err = fd.Close() |
| 521 | if err != nil { |
| 522 | return errors.Wrap(ctx, err, "close file") |
| 523 | } |
| 524 | err = multipartFile.Close() |
| 525 | if err != nil { |
| 526 | return errors.Wrap(ctx, err, "close multipart form") |
| 527 | } |
| 528 | |
| 529 | req, err := http.NewRequest("POST", endpoint, body) |
| 530 | if err != nil { |
| 531 | return errors.Wrap(ctx, err, "create request") |
| 532 | } |
| 533 | |
| 534 | token, err := runCtx.scalingoClient.GetAccessToken(ctx) |
| 535 | if err != nil { |
| 536 | return errors.Wrap(ctx, err, "generate access token") |
| 537 | } |
| 538 | req.SetBasicAuth("", token) |
| 539 | |
| 540 | req.Header.Set("Content-Type", multipartFile.FormDataContentType()) |
| 541 | |
| 542 | fmt.Fprintln(runCtx.waitingTextOutputWriter, "Upload", file, "to container.") |
| 543 | debug.Println("Endpoint:", req.URL) |
| 544 | |
| 545 | res, err := httpclient.Do(req) |
| 546 | if err != nil { |
| 547 | return errors.Wrap(ctx, err, "send request") |
| 548 | } |
| 549 | defer res.Body.Close() |
| 550 | if res.StatusCode != 200 { |
| 551 | b, _ := stdio.ReadAll(res.Body) |
| 552 | return errors.Newf(ctx, "invalid status code %v (%s)", res.Status, strings.TrimSpace(string(b))) |
| 553 | } |
| 554 | return nil |
| 555 | } |