Merge pull request #10820 from chrisroberts/force-no-direct-upload-to-vagrantcloud

Override direct upload based on box size
This commit is contained in:
Megan Marsh 2021-03-24 14:50:49 -07:00 committed by GitHub
commit 755395faf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 177 additions and 6 deletions

View File

@ -188,10 +188,10 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa
new(stepCreateProvider),
}
if p.config.BoxDownloadUrl == "" {
steps = append(steps, new(stepPrepareUpload), new(stepUpload))
if !p.config.NoDirectUpload {
steps = append(steps, new(stepConfirmUpload))
}
steps = append(steps,
new(stepPrepareUpload),
new(stepUpload),
new(stepConfirmUpload))
}
steps = append(steps, new(stepReleaseVersion))

View File

@ -10,6 +10,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"runtime"
"strings"
"testing"
@ -361,6 +362,138 @@ func TestPostProcessor_PostProcess_uploadsAndNoRelease(t *testing.T) {
}
}
func TestPostProcessor_PostProcess_directUpload5GFile(t *testing.T) {
// Disable test on Windows due to unreliable sparse file creation
if runtime.GOOS == "windows" {
return
}
// Boxes up to 5GB are supported for direct upload so
// set the box asset to be 5GB exactly
fSize := int64(5368709120)
files := tarFiles{
{"foo.txt", "This is a foo file"},
{"bar.txt", "This is a bar file"},
{"metadata.json", `{"provider": "virtualbox"}`},
}
f, err := createBox(files)
if err != nil {
t.Fatalf("%s", err)
}
defer os.Remove(f.Name())
if err := expandFile(f, fSize); err != nil {
t.Fatalf("failed to expand box file - %s", err)
}
artifact := &packersdk.MockArtifact{
BuilderIdValue: "mitchellh.post-processor.vagrant",
FilesValue: []string{f.Name()},
}
f.Close()
s := newStackServer(
[]stubResponse{
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-path"},
},
)
defer s.Close()
stack := []stubResponse{
stubResponse{StatusCode: 200, Method: "GET", Path: "/authenticate"},
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64", Response: `{"tag": "hashicorp/precise64"}`},
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/versions", Response: `{}`},
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/version/0.5/providers", Response: `{}`},
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64/version/0.5/provider/id/upload/direct"},
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-complete"},
}
server := newStackServer(stack)
defer server.Close()
config := testGoodConfig()
config["vagrant_cloud_url"] = server.URL
config["no_release"] = true
// Set response here so we have API server URL available
stack[4].Response = `{"upload_path": "` + s.URL + `/box-upload-path", "callback": "` + server.URL + `/box-upload-complete"}`
var p PostProcessor
err = p.Configure(config)
if err != nil {
t.Fatalf("err: %s", err)
}
_, _, _, err = p.PostProcess(context.Background(), testUi(), artifact)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestPostProcessor_PostProcess_directUploadOver5GFile(t *testing.T) {
// Disable test on Windows due to unreliable sparse file creation
if runtime.GOOS == "windows" {
return
}
// Boxes over 5GB are not supported for direct upload so
// set the box asset to be one byte over 5GB
fSize := int64(5368709121)
files := tarFiles{
{"foo.txt", "This is a foo file"},
{"bar.txt", "This is a bar file"},
{"metadata.json", `{"provider": "virtualbox"}`},
}
f, err := createBox(files)
if err != nil {
t.Fatalf("%s", err)
}
defer os.Remove(f.Name())
if err := expandFile(f, fSize); err != nil {
t.Fatalf("failed to expand box file - %s", err)
}
f.Close()
artifact := &packersdk.MockArtifact{
BuilderIdValue: "mitchellh.post-processor.vagrant",
FilesValue: []string{f.Name()},
}
s := newStackServer(
[]stubResponse{
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-path"},
},
)
defer s.Close()
stack := []stubResponse{
stubResponse{StatusCode: 200, Method: "GET", Path: "/authenticate"},
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64", Response: `{"tag": "hashicorp/precise64"}`},
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/versions", Response: `{}`},
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/version/0.5/providers", Response: `{}`},
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64/version/0.5/provider/id/upload", Response: `{"upload_path": "` + s.URL + `/box-upload-path"}`},
}
server := newStackServer(stack)
defer server.Close()
config := testGoodConfig()
config["vagrant_cloud_url"] = server.URL
config["no_release"] = true
var p PostProcessor
err = p.Configure(config)
if err != nil {
t.Fatalf("err: %s", err)
}
_, _, _, err = p.PostProcess(context.Background(), testUi(), artifact)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestPostProcessor_PostProcess_uploadsDirectAndReleases(t *testing.T) {
files := tarFiles{
{"foo.txt", "This is a foo file"},
@ -697,3 +830,21 @@ func createBox(files tarFiles) (boxfile *os.File, err error) {
return boxfile, nil
}
func expandFile(f *os.File, finalSize int64) (err error) {
s, err := f.Stat()
if err != nil {
return
}
size := finalSize - s.Size()
if size < 1 {
return
}
if _, err = f.Seek(size-1, 2); err != nil {
return
}
if _, err = f.Write([]byte{0}); err != nil {
return
}
return nil
}

View File

@ -16,6 +16,11 @@ func (s *stepConfirmUpload) Run(ctx context.Context, state multistep.StateBag) m
ui := state.Get("ui").(packersdk.Ui)
upload := state.Get("upload").(*Upload)
url := upload.CallbackPath
config := state.Get("config").(*Config)
if config.NoDirectUpload {
return multistep.ActionContinue
}
ui.Say("Confirming direct box upload completion")
@ -23,7 +28,7 @@ func (s *stepConfirmUpload) Run(ctx context.Context, state multistep.StateBag) m
if err != nil || resp.StatusCode != 200 {
if resp == nil || resp.Body == nil {
state.Put("error", "No response from server.")
state.Put("error", fmt.Errorf("No response from server."))
} else {
cloudErrors := &VagrantCloudErrors{}
err = decodeBody(resp, cloudErrors)

View File

@ -3,11 +3,14 @@ package vagrantcloud
import (
"context"
"fmt"
"os"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const VAGRANT_CLOUD_DIRECT_UPLOAD_LIMIT = 5368709120 // Upload limit is 5G
type Upload struct {
UploadPath string `json:"upload_path"`
CallbackPath string `json:"callback"`
@ -25,6 +28,18 @@ func (s *stepPrepareUpload) Run(ctx context.Context, state multistep.StateBag) m
provider := state.Get("provider").(*Provider)
artifactFilePath := state.Get("artifactFilePath").(string)
// If direct upload is enabled, the asset size must be <= 5 GB
if config.NoDirectUpload == false {
f, err := os.Stat(artifactFilePath)
if err != nil {
ui.Error(fmt.Sprintf("error determining size of upload artifact: %s", artifactFilePath))
}
if f.Size() > VAGRANT_CLOUD_DIRECT_UPLOAD_LIMIT {
ui.Say(fmt.Sprintf("Asset %s is larger than the direct upload limit. Setting `NoDirectUpload` to true", artifactFilePath))
config.NoDirectUpload = true
}
}
path := fmt.Sprintf("box/%s/version/%v/provider/%s/upload", box.Tag, version.Version, provider.Name)
if !config.NoDirectUpload {
path = path + "/direct"
@ -37,7 +52,7 @@ func (s *stepPrepareUpload) Run(ctx context.Context, state multistep.StateBag) m
if err != nil || (resp.StatusCode != 200) {
if resp == nil || resp.Body == nil {
state.Put("error", "No response from server.")
state.Put("error", fmt.Errorf("No response from server."))
} else {
cloudErrors := &VagrantCloudErrors{}
err = decodeBody(resp, cloudErrors)