From 01c7bbf8c4e28d8b38a935a69395b92bc2c6b900 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 14:25:22 -0800 Subject: [PATCH 01/30] builder/virtualbox: move to iso/ folder --- .gitignore | 1 + builder/virtualbox/{ => iso}/artifact.go | 0 builder/virtualbox/{ => iso}/builder.go | 0 builder/virtualbox/{ => iso}/builder_test.go | 0 builder/virtualbox/{ => iso}/driver.go | 0 builder/virtualbox/{ => iso}/ssh.go | 0 builder/virtualbox/{ => iso}/step_attach_floppy.go | 0 builder/virtualbox/{ => iso}/step_attach_guest_additions.go | 0 builder/virtualbox/{ => iso}/step_attach_iso.go | 0 builder/virtualbox/{ => iso}/step_create_disk.go | 0 builder/virtualbox/{ => iso}/step_create_vm.go | 0 builder/virtualbox/{ => iso}/step_download_guest_additions.go | 0 builder/virtualbox/{ => iso}/step_export.go | 0 builder/virtualbox/{ => iso}/step_forward_ssh.go | 0 builder/virtualbox/{ => iso}/step_http_server.go | 0 builder/virtualbox/{ => iso}/step_prepare_output_dir.go | 0 builder/virtualbox/{ => iso}/step_remove_devices.go | 0 builder/virtualbox/{ => iso}/step_run.go | 0 builder/virtualbox/{ => iso}/step_shutdown.go | 0 builder/virtualbox/{ => iso}/step_suppress_messages.go | 0 builder/virtualbox/{ => iso}/step_type_boot_command.go | 0 builder/virtualbox/{ => iso}/step_upload_guest_additions.go | 0 builder/virtualbox/{ => iso}/step_upload_version.go | 0 builder/virtualbox/{ => iso}/step_vboxmanage.go | 0 24 files changed, 1 insertion(+) rename builder/virtualbox/{ => iso}/artifact.go (100%) rename builder/virtualbox/{ => iso}/builder.go (100%) rename builder/virtualbox/{ => iso}/builder_test.go (100%) rename builder/virtualbox/{ => iso}/driver.go (100%) rename builder/virtualbox/{ => iso}/ssh.go (100%) rename builder/virtualbox/{ => iso}/step_attach_floppy.go (100%) rename builder/virtualbox/{ => iso}/step_attach_guest_additions.go (100%) rename builder/virtualbox/{ => iso}/step_attach_iso.go (100%) rename builder/virtualbox/{ => iso}/step_create_disk.go (100%) rename builder/virtualbox/{ => iso}/step_create_vm.go (100%) rename builder/virtualbox/{ => iso}/step_download_guest_additions.go (100%) rename builder/virtualbox/{ => iso}/step_export.go (100%) rename builder/virtualbox/{ => iso}/step_forward_ssh.go (100%) rename builder/virtualbox/{ => iso}/step_http_server.go (100%) rename builder/virtualbox/{ => iso}/step_prepare_output_dir.go (100%) rename builder/virtualbox/{ => iso}/step_remove_devices.go (100%) rename builder/virtualbox/{ => iso}/step_run.go (100%) rename builder/virtualbox/{ => iso}/step_shutdown.go (100%) rename builder/virtualbox/{ => iso}/step_suppress_messages.go (100%) rename builder/virtualbox/{ => iso}/step_type_boot_command.go (100%) rename builder/virtualbox/{ => iso}/step_upload_guest_additions.go (100%) rename builder/virtualbox/{ => iso}/step_upload_version.go (100%) rename builder/virtualbox/{ => iso}/step_vboxmanage.go (100%) diff --git a/.gitignore b/.gitignore index 279aabcb4..d9d37c96f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /src /website/.sass-cache /website/build +.DS_Store .vagrant Vagrantfile test/.env diff --git a/builder/virtualbox/artifact.go b/builder/virtualbox/iso/artifact.go similarity index 100% rename from builder/virtualbox/artifact.go rename to builder/virtualbox/iso/artifact.go diff --git a/builder/virtualbox/builder.go b/builder/virtualbox/iso/builder.go similarity index 100% rename from builder/virtualbox/builder.go rename to builder/virtualbox/iso/builder.go diff --git a/builder/virtualbox/builder_test.go b/builder/virtualbox/iso/builder_test.go similarity index 100% rename from builder/virtualbox/builder_test.go rename to builder/virtualbox/iso/builder_test.go diff --git a/builder/virtualbox/driver.go b/builder/virtualbox/iso/driver.go similarity index 100% rename from builder/virtualbox/driver.go rename to builder/virtualbox/iso/driver.go diff --git a/builder/virtualbox/ssh.go b/builder/virtualbox/iso/ssh.go similarity index 100% rename from builder/virtualbox/ssh.go rename to builder/virtualbox/iso/ssh.go diff --git a/builder/virtualbox/step_attach_floppy.go b/builder/virtualbox/iso/step_attach_floppy.go similarity index 100% rename from builder/virtualbox/step_attach_floppy.go rename to builder/virtualbox/iso/step_attach_floppy.go diff --git a/builder/virtualbox/step_attach_guest_additions.go b/builder/virtualbox/iso/step_attach_guest_additions.go similarity index 100% rename from builder/virtualbox/step_attach_guest_additions.go rename to builder/virtualbox/iso/step_attach_guest_additions.go diff --git a/builder/virtualbox/step_attach_iso.go b/builder/virtualbox/iso/step_attach_iso.go similarity index 100% rename from builder/virtualbox/step_attach_iso.go rename to builder/virtualbox/iso/step_attach_iso.go diff --git a/builder/virtualbox/step_create_disk.go b/builder/virtualbox/iso/step_create_disk.go similarity index 100% rename from builder/virtualbox/step_create_disk.go rename to builder/virtualbox/iso/step_create_disk.go diff --git a/builder/virtualbox/step_create_vm.go b/builder/virtualbox/iso/step_create_vm.go similarity index 100% rename from builder/virtualbox/step_create_vm.go rename to builder/virtualbox/iso/step_create_vm.go diff --git a/builder/virtualbox/step_download_guest_additions.go b/builder/virtualbox/iso/step_download_guest_additions.go similarity index 100% rename from builder/virtualbox/step_download_guest_additions.go rename to builder/virtualbox/iso/step_download_guest_additions.go diff --git a/builder/virtualbox/step_export.go b/builder/virtualbox/iso/step_export.go similarity index 100% rename from builder/virtualbox/step_export.go rename to builder/virtualbox/iso/step_export.go diff --git a/builder/virtualbox/step_forward_ssh.go b/builder/virtualbox/iso/step_forward_ssh.go similarity index 100% rename from builder/virtualbox/step_forward_ssh.go rename to builder/virtualbox/iso/step_forward_ssh.go diff --git a/builder/virtualbox/step_http_server.go b/builder/virtualbox/iso/step_http_server.go similarity index 100% rename from builder/virtualbox/step_http_server.go rename to builder/virtualbox/iso/step_http_server.go diff --git a/builder/virtualbox/step_prepare_output_dir.go b/builder/virtualbox/iso/step_prepare_output_dir.go similarity index 100% rename from builder/virtualbox/step_prepare_output_dir.go rename to builder/virtualbox/iso/step_prepare_output_dir.go diff --git a/builder/virtualbox/step_remove_devices.go b/builder/virtualbox/iso/step_remove_devices.go similarity index 100% rename from builder/virtualbox/step_remove_devices.go rename to builder/virtualbox/iso/step_remove_devices.go diff --git a/builder/virtualbox/step_run.go b/builder/virtualbox/iso/step_run.go similarity index 100% rename from builder/virtualbox/step_run.go rename to builder/virtualbox/iso/step_run.go diff --git a/builder/virtualbox/step_shutdown.go b/builder/virtualbox/iso/step_shutdown.go similarity index 100% rename from builder/virtualbox/step_shutdown.go rename to builder/virtualbox/iso/step_shutdown.go diff --git a/builder/virtualbox/step_suppress_messages.go b/builder/virtualbox/iso/step_suppress_messages.go similarity index 100% rename from builder/virtualbox/step_suppress_messages.go rename to builder/virtualbox/iso/step_suppress_messages.go diff --git a/builder/virtualbox/step_type_boot_command.go b/builder/virtualbox/iso/step_type_boot_command.go similarity index 100% rename from builder/virtualbox/step_type_boot_command.go rename to builder/virtualbox/iso/step_type_boot_command.go diff --git a/builder/virtualbox/step_upload_guest_additions.go b/builder/virtualbox/iso/step_upload_guest_additions.go similarity index 100% rename from builder/virtualbox/step_upload_guest_additions.go rename to builder/virtualbox/iso/step_upload_guest_additions.go diff --git a/builder/virtualbox/step_upload_version.go b/builder/virtualbox/iso/step_upload_version.go similarity index 100% rename from builder/virtualbox/step_upload_version.go rename to builder/virtualbox/iso/step_upload_version.go diff --git a/builder/virtualbox/step_vboxmanage.go b/builder/virtualbox/iso/step_vboxmanage.go similarity index 100% rename from builder/virtualbox/step_vboxmanage.go rename to builder/virtualbox/iso/step_vboxmanage.go From afd67ea10419cb27e10aa6e4032ef9c67f39d81b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 14:27:00 -0800 Subject: [PATCH 02/30] builder/virtualbox/iso: rename package --- builder/virtualbox/iso/artifact.go | 2 +- builder/virtualbox/iso/builder.go | 2 +- builder/virtualbox/iso/builder_test.go | 2 +- builder/virtualbox/iso/driver.go | 2 +- builder/virtualbox/iso/ssh.go | 2 +- builder/virtualbox/iso/step_attach_floppy.go | 2 +- builder/virtualbox/iso/step_attach_guest_additions.go | 2 +- builder/virtualbox/iso/step_attach_iso.go | 2 +- builder/virtualbox/iso/step_create_disk.go | 2 +- builder/virtualbox/iso/step_create_vm.go | 2 +- builder/virtualbox/iso/step_download_guest_additions.go | 2 +- builder/virtualbox/iso/step_export.go | 2 +- builder/virtualbox/iso/step_forward_ssh.go | 2 +- builder/virtualbox/iso/step_http_server.go | 2 +- builder/virtualbox/iso/step_prepare_output_dir.go | 2 +- builder/virtualbox/iso/step_remove_devices.go | 2 +- builder/virtualbox/iso/step_run.go | 2 +- builder/virtualbox/iso/step_shutdown.go | 2 +- builder/virtualbox/iso/step_suppress_messages.go | 2 +- builder/virtualbox/iso/step_type_boot_command.go | 2 +- builder/virtualbox/iso/step_upload_guest_additions.go | 2 +- builder/virtualbox/iso/step_upload_version.go | 2 +- builder/virtualbox/iso/step_vboxmanage.go | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/builder/virtualbox/iso/artifact.go b/builder/virtualbox/iso/artifact.go index a31b14aee..afbdf584f 100644 --- a/builder/virtualbox/iso/artifact.go +++ b/builder/virtualbox/iso/artifact.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 1091858ec..d89715d16 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "errors" diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 942bc059f..0874c4b60 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "github.com/mitchellh/packer/packer" diff --git a/builder/virtualbox/iso/driver.go b/builder/virtualbox/iso/driver.go index 09ace38d9..6e1b79c8d 100644 --- a/builder/virtualbox/iso/driver.go +++ b/builder/virtualbox/iso/driver.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "bytes" diff --git a/builder/virtualbox/iso/ssh.go b/builder/virtualbox/iso/ssh.go index b685a545f..2eb3cc8ab 100644 --- a/builder/virtualbox/iso/ssh.go +++ b/builder/virtualbox/iso/ssh.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( gossh "code.google.com/p/go.crypto/ssh" diff --git a/builder/virtualbox/iso/step_attach_floppy.go b/builder/virtualbox/iso/step_attach_floppy.go index 8f41441a5..86a0b7c48 100644 --- a/builder/virtualbox/iso/step_attach_floppy.go +++ b/builder/virtualbox/iso/step_attach_floppy.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_attach_guest_additions.go b/builder/virtualbox/iso/step_attach_guest_additions.go index c0710bc16..a7832a768 100644 --- a/builder/virtualbox/iso/step_attach_guest_additions.go +++ b/builder/virtualbox/iso/step_attach_guest_additions.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_attach_iso.go b/builder/virtualbox/iso/step_attach_iso.go index ee45647b1..3eeeeec6a 100644 --- a/builder/virtualbox/iso/step_attach_iso.go +++ b/builder/virtualbox/iso/step_attach_iso.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_create_disk.go b/builder/virtualbox/iso/step_create_disk.go index f0d803295..64dce39b3 100644 --- a/builder/virtualbox/iso/step_create_disk.go +++ b/builder/virtualbox/iso/step_create_disk.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_create_vm.go b/builder/virtualbox/iso/step_create_vm.go index 3453c168b..4b88ac8ce 100644 --- a/builder/virtualbox/iso/step_create_vm.go +++ b/builder/virtualbox/iso/step_create_vm.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_download_guest_additions.go b/builder/virtualbox/iso/step_download_guest_additions.go index 6dd2c9cca..5905dfed7 100644 --- a/builder/virtualbox/iso/step_download_guest_additions.go +++ b/builder/virtualbox/iso/step_download_guest_additions.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "bytes" diff --git a/builder/virtualbox/iso/step_export.go b/builder/virtualbox/iso/step_export.go index 3a875c5d7..37d113410 100644 --- a/builder/virtualbox/iso/step_export.go +++ b/builder/virtualbox/iso/step_export.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_forward_ssh.go b/builder/virtualbox/iso/step_forward_ssh.go index 55a46611e..351513a11 100644 --- a/builder/virtualbox/iso/step_forward_ssh.go +++ b/builder/virtualbox/iso/step_forward_ssh.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_http_server.go b/builder/virtualbox/iso/step_http_server.go index ad3e9f755..24da8dd9d 100644 --- a/builder/virtualbox/iso/step_http_server.go +++ b/builder/virtualbox/iso/step_http_server.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_prepare_output_dir.go b/builder/virtualbox/iso/step_prepare_output_dir.go index 4a3e6c844..42c9124b5 100644 --- a/builder/virtualbox/iso/step_prepare_output_dir.go +++ b/builder/virtualbox/iso/step_prepare_output_dir.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_remove_devices.go b/builder/virtualbox/iso/step_remove_devices.go index f0f4a46c3..4fcaeb6e4 100644 --- a/builder/virtualbox/iso/step_remove_devices.go +++ b/builder/virtualbox/iso/step_remove_devices.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_run.go b/builder/virtualbox/iso/step_run.go index 1a44c10c2..de4936dd9 100644 --- a/builder/virtualbox/iso/step_run.go +++ b/builder/virtualbox/iso/step_run.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_shutdown.go b/builder/virtualbox/iso/step_shutdown.go index 713368e8c..7c36a2534 100644 --- a/builder/virtualbox/iso/step_shutdown.go +++ b/builder/virtualbox/iso/step_shutdown.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "errors" diff --git a/builder/virtualbox/iso/step_suppress_messages.go b/builder/virtualbox/iso/step_suppress_messages.go index 4ed7f1939..595d97004 100644 --- a/builder/virtualbox/iso/step_suppress_messages.go +++ b/builder/virtualbox/iso/step_suppress_messages.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_type_boot_command.go b/builder/virtualbox/iso/step_type_boot_command.go index ec3df82a6..f0680efe9 100644 --- a/builder/virtualbox/iso/step_type_boot_command.go +++ b/builder/virtualbox/iso/step_type_boot_command.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_upload_guest_additions.go b/builder/virtualbox/iso/step_upload_guest_additions.go index 2dce09d0e..839943125 100644 --- a/builder/virtualbox/iso/step_upload_guest_additions.go +++ b/builder/virtualbox/iso/step_upload_guest_additions.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" diff --git a/builder/virtualbox/iso/step_upload_version.go b/builder/virtualbox/iso/step_upload_version.go index c22a08117..b84b5d948 100644 --- a/builder/virtualbox/iso/step_upload_version.go +++ b/builder/virtualbox/iso/step_upload_version.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "bytes" diff --git a/builder/virtualbox/iso/step_vboxmanage.go b/builder/virtualbox/iso/step_vboxmanage.go index 2bc41b90b..e5c80f4b5 100644 --- a/builder/virtualbox/iso/step_vboxmanage.go +++ b/builder/virtualbox/iso/step_vboxmanage.go @@ -1,4 +1,4 @@ -package virtualbox +package iso import ( "fmt" From 09c3d67c51d64df3e8b6ad3b13bcbe28d4c5be85 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 14:27:23 -0800 Subject: [PATCH 03/30] plugin/builder-virtualbox: new package --- plugin/builder-virtualbox/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/builder-virtualbox/main.go b/plugin/builder-virtualbox/main.go index 136b2698e..96271b811 100644 --- a/plugin/builder-virtualbox/main.go +++ b/plugin/builder-virtualbox/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/mitchellh/packer/builder/virtualbox" + "github.com/mitchellh/packer/builder/virtualbox/iso" "github.com/mitchellh/packer/packer/plugin" ) @@ -10,6 +10,6 @@ func main() { if err != nil { panic(err) } - server.RegisterBuilder(new(virtualbox.Builder)) + server.RegisterBuilder(new(iso.Builder)) server.Serve() } From 14eede264fea66621900b99232c2cca218b06e52 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 14:51:38 -0800 Subject: [PATCH 04/30] builder/virtualbox: add common package, common Artifact --- builder/virtualbox/common/artifact.go | 61 ++++++++++++++++++++++ builder/virtualbox/common/artifact_test.go | 43 +++++++++++++++ builder/virtualbox/iso/artifact.go | 33 ------------ builder/virtualbox/iso/builder.go | 22 +------- 4 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 builder/virtualbox/common/artifact.go create mode 100644 builder/virtualbox/common/artifact_test.go delete mode 100644 builder/virtualbox/iso/artifact.go diff --git a/builder/virtualbox/common/artifact.go b/builder/virtualbox/common/artifact.go new file mode 100644 index 000000000..f4fc48100 --- /dev/null +++ b/builder/virtualbox/common/artifact.go @@ -0,0 +1,61 @@ +package common + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/mitchellh/packer/packer" +) + +// This is the common builder ID to all of these artifacts. +const BuilderId = "mitchellh.virtualbox" + +// Artifact is the result of running the VirtualBox builder, namely a set +// of files associated with the resulting machine. +type artifact struct { + dir string + f []string +} + +// NewArtifact returns a VirtualBox artifact containing the files +// in the given directory. +func NewArtifact(dir string) (packer.Artifact, error) { + files := make([]string, 0, 5) + visit := func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + files = append(files, path) + } + + return err + } + + if err := filepath.Walk(dir, visit); err != nil { + return nil, err + } + + return &artifact{ + dir: dir, + f: files, + }, nil +} + +func (*artifact) BuilderId() string { + return BuilderId +} + +func (a *artifact) Files() []string { + return a.f +} + +func (*artifact) Id() string { + return "VM" +} + +func (a *artifact) String() string { + return fmt.Sprintf("VM files in directory: %s", a.dir) +} + +func (a *artifact) Destroy() error { + return os.RemoveAll(a.dir) +} diff --git a/builder/virtualbox/common/artifact_test.go b/builder/virtualbox/common/artifact_test.go new file mode 100644 index 000000000..f9ddc5dbf --- /dev/null +++ b/builder/virtualbox/common/artifact_test.go @@ -0,0 +1,43 @@ +package common + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/mitchellh/packer/packer" +) + +func TestArtifact_impl(t *testing.T) { + var _ packer.Artifact = new(artifact) +} + +func TestNewArtifact(t *testing.T) { + td, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(td) + + err = ioutil.WriteFile(filepath.Join(td, "a"), []byte("foo"), 0644) + if err != nil { + t.Fatalf("err: %s", err) + } + + if err := os.Mkdir(filepath.Join(td, "b"), 0755); err != nil { + t.Fatalf("err: %s", err) + } + + a, err := NewArtifact(td) + if err != nil { + t.Fatalf("err: %s", err) + } + + if a.BuilderId() != BuilderId { + t.Fatalf("bad: %#v", a.BuilderId()) + } + if len(a.Files()) != 1 { + t.Fatalf("should length 1: %d", len(a.Files())) + } +} diff --git a/builder/virtualbox/iso/artifact.go b/builder/virtualbox/iso/artifact.go deleted file mode 100644 index afbdf584f..000000000 --- a/builder/virtualbox/iso/artifact.go +++ /dev/null @@ -1,33 +0,0 @@ -package iso - -import ( - "fmt" - "os" -) - -// Artifact is the result of running the VirtualBox builder, namely a set -// of files associated with the resulting machine. -type Artifact struct { - dir string - f []string -} - -func (*Artifact) BuilderId() string { - return BuilderId -} - -func (a *Artifact) Files() []string { - return a.f -} - -func (*Artifact) Id() string { - return "VM" -} - -func (a *Artifact) String() string { - return fmt.Sprintf("VM files in directory: %s", a.dir) -} - -func (a *Artifact) Destroy() error { - return os.RemoveAll(a.dir) -} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index d89715d16..9cbc9dd92 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "log" @@ -457,26 +458,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, errors.New("Build was halted.") } - // Compile the artifact list - files := make([]string, 0, 5) - visit := func(path string, info os.FileInfo, err error) error { - if !info.IsDir() { - files = append(files, path) - } - - return err - } - - if err := filepath.Walk(b.config.OutputDir, visit); err != nil { - return nil, err - } - - artifact := &Artifact{ - dir: b.config.OutputDir, - f: files, - } - - return artifact, nil + return vboxcommon.NewArtifact(b.config.OutputDir) } func (b *Builder) Cancel() { From cdc02db96def681e8488576b63293a737f6d7443 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 15:00:48 -0800 Subject: [PATCH 05/30] builder/virtualbox/common: Move Driver here --- builder/virtualbox/common/driver.go | 77 +++++++++++++++++++ .../{iso/driver.go => common/driver_4_2.go} | 34 +------- builder/virtualbox/common/driver_4_2_test.go | 9 +++ builder/virtualbox/iso/builder.go | 40 +--------- builder/virtualbox/iso/step_attach_floppy.go | 5 +- .../iso/step_attach_guest_additions.go | 5 +- builder/virtualbox/iso/step_attach_iso.go | 5 +- builder/virtualbox/iso/step_create_disk.go | 3 +- builder/virtualbox/iso/step_create_vm.go | 5 +- .../iso/step_download_guest_additions.go | 3 +- builder/virtualbox/iso/step_export.go | 3 +- builder/virtualbox/iso/step_forward_ssh.go | 3 +- builder/virtualbox/iso/step_remove_devices.go | 3 +- builder/virtualbox/iso/step_run.go | 5 +- builder/virtualbox/iso/step_shutdown.go | 3 +- .../virtualbox/iso/step_suppress_messages.go | 3 +- .../virtualbox/iso/step_type_boot_command.go | 3 +- .../iso/step_upload_guest_additions.go | 3 +- builder/virtualbox/iso/step_upload_version.go | 3 +- builder/virtualbox/iso/step_vboxmanage.go | 3 +- 20 files changed, 125 insertions(+), 93 deletions(-) create mode 100644 builder/virtualbox/common/driver.go rename builder/virtualbox/{iso/driver.go => common/driver_4_2.go} (76%) create mode 100644 builder/virtualbox/common/driver_4_2_test.go diff --git a/builder/virtualbox/common/driver.go b/builder/virtualbox/common/driver.go new file mode 100644 index 000000000..75b259ac3 --- /dev/null +++ b/builder/virtualbox/common/driver.go @@ -0,0 +1,77 @@ +package common + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +// A driver is able to talk to VirtualBox and perform certain +// operations with it. Some of the operations on here may seem overly +// specific, but they were built specifically in mind to handle features +// of the VirtualBox builder for Packer, and to abstract differences in +// versions out of the builder steps, so sometimes the methods are +// extremely specific. +type Driver interface { + // Create a SATA controller. + CreateSATAController(vm string, controller string) error + + // Checks if the VM with the given name is running. + IsRunning(string) (bool, error) + + // Stop stops a running machine, forcefully. + Stop(string) error + + // SuppressMessages should do what needs to be done in order to + // suppress any annoying popups from VirtualBox. + SuppressMessages() error + + // VBoxManage executes the given VBoxManage command + VBoxManage(...string) error + + // Verify checks to make sure that this driver should function + // properly. If there is any indication the driver can't function, + // this will return an error. + Verify() error + + // Version reads the version of VirtualBox that is installed. + Version() (string, error) +} + +func NewDriver() (Driver, error) { + var vboxmanagePath string + + // On Windows, we check VBOX_INSTALL_PATH env var for the path + if runtime.GOOS == "windows" { + if installPath := os.Getenv("VBOX_INSTALL_PATH"); installPath != "" { + log.Printf("[DEBUG] builder/virtualbox: VBOX_INSTALL_PATH: %s", + installPath) + for _, path := range strings.Split(installPath, ";") { + path = filepath.Join(path, "VBoxManage.exe") + if _, err := os.Stat(path); err == nil { + vboxmanagePath = path + break + } + } + } + } + + if vboxmanagePath == "" { + var err error + vboxmanagePath, err = exec.LookPath("VBoxManage") + if err != nil { + return nil, err + } + } + + log.Printf("VBoxManage path: %s", vboxmanagePath) + driver := &VBox42Driver{vboxmanagePath} + if err := driver.Verify(); err != nil { + return nil, err + } + + return driver, nil +} diff --git a/builder/virtualbox/iso/driver.go b/builder/virtualbox/common/driver_4_2.go similarity index 76% rename from builder/virtualbox/iso/driver.go rename to builder/virtualbox/common/driver_4_2.go index 6e1b79c8d..9b937faf9 100644 --- a/builder/virtualbox/iso/driver.go +++ b/builder/virtualbox/common/driver_4_2.go @@ -1,4 +1,4 @@ -package iso +package common import ( "bytes" @@ -10,38 +10,6 @@ import ( "time" ) -// A driver is able to talk to VirtualBox and perform certain -// operations with it. Some of the operations on here may seem overly -// specific, but they were built specifically in mind to handle features -// of the VirtualBox builder for Packer, and to abstract differences in -// versions out of the builder steps, so sometimes the methods are -// extremely specific. -type Driver interface { - // Create a SATA controller. - CreateSATAController(vm string, controller string) error - - // Checks if the VM with the given name is running. - IsRunning(string) (bool, error) - - // Stop stops a running machine, forcefully. - Stop(string) error - - // SuppressMessages should do what needs to be done in order to - // suppress any annoying popups from VirtualBox. - SuppressMessages() error - - // VBoxManage executes the given VBoxManage command - VBoxManage(...string) error - - // Verify checks to make sure that this driver should function - // properly. If there is any indication the driver can't function, - // this will return an error. - Verify() error - - // Version reads the version of VirtualBox that is installed. - Version() (string, error) -} - type VBox42Driver struct { // This is the path to the "VBoxManage" application. VBoxManagePath string diff --git a/builder/virtualbox/common/driver_4_2_test.go b/builder/virtualbox/common/driver_4_2_test.go new file mode 100644 index 000000000..dc92ddd7c --- /dev/null +++ b/builder/virtualbox/common/driver_4_2_test.go @@ -0,0 +1,9 @@ +package common + +import ( + "testing" +) + +func TestVBox42Driver_impl(t *testing.T) { + var _ Driver = new(VBox42Driver) +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 9cbc9dd92..d2c9ff4b2 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -9,9 +9,6 @@ import ( "github.com/mitchellh/packer/packer" "log" "os" - "os/exec" - "path/filepath" - "runtime" "strings" "time" ) @@ -382,7 +379,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { // Create the driver that we'll use to communicate with VirtualBox - driver, err := b.newDriver() + driver, err := vboxcommon.NewDriver() if err != nil { return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err) } @@ -467,38 +464,3 @@ func (b *Builder) Cancel() { b.runner.Cancel() } } - -func (b *Builder) newDriver() (Driver, error) { - var vboxmanagePath string - - if runtime.GOOS == "windows" { - // On Windows, we check VBOX_INSTALL_PATH env var for the path - if installPath := os.Getenv("VBOX_INSTALL_PATH"); installPath != "" { - log.Printf("[DEBUG] builder/virtualbox: VBOX_INSTALL_PATH: %s", - installPath) - for _, path := range strings.Split(installPath, ";") { - path = filepath.Join(path, "VBoxManage.exe") - if _, err := os.Stat(path); err == nil { - vboxmanagePath = path - break - } - } - } - } - - if vboxmanagePath == "" { - var err error - vboxmanagePath, err = exec.LookPath("VBoxManage") - if err != nil { - return nil, err - } - } - - log.Printf("VBoxManage path: %s", vboxmanagePath) - driver := &VBox42Driver{vboxmanagePath} - if err := driver.Verify(); err != nil { - return nil, err - } - - return driver, nil -} diff --git a/builder/virtualbox/iso/step_attach_floppy.go b/builder/virtualbox/iso/step_attach_floppy.go index 86a0b7c48..e784a0fc0 100644 --- a/builder/virtualbox/iso/step_attach_floppy.go +++ b/builder/virtualbox/iso/step_attach_floppy.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "io" "io/ioutil" @@ -39,7 +40,7 @@ func (s *stepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -84,7 +85,7 @@ func (s *stepAttachFloppy) Cleanup(state multistep.StateBag) { // Delete the floppy disk defer os.Remove(s.floppyPath) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) vmName := state.Get("vmName").(string) command := []string{ diff --git a/builder/virtualbox/iso/step_attach_guest_additions.go b/builder/virtualbox/iso/step_attach_guest_additions.go index a7832a768..f35710259 100644 --- a/builder/virtualbox/iso/step_attach_guest_additions.go +++ b/builder/virtualbox/iso/step_attach_guest_additions.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" ) @@ -24,7 +25,7 @@ type stepAttachGuestAdditions struct { func (s *stepAttachGuestAdditions) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -65,7 +66,7 @@ func (s *stepAttachGuestAdditions) Cleanup(state multistep.StateBag) { return } - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_attach_iso.go b/builder/virtualbox/iso/step_attach_iso.go index 3eeeeec6a..5d4ec42ef 100644 --- a/builder/virtualbox/iso/step_attach_iso.go +++ b/builder/virtualbox/iso/step_attach_iso.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" ) @@ -16,7 +17,7 @@ type stepAttachISO struct { } func (s *stepAttachISO) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) isoPath := state.Get("iso_path").(string) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -48,7 +49,7 @@ func (s *stepAttachISO) Cleanup(state multistep.StateBag) { return } - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) vmName := state.Get("vmName").(string) command := []string{ diff --git a/builder/virtualbox/iso/step_create_disk.go b/builder/virtualbox/iso/step_create_disk.go index 64dce39b3..6875d7b33 100644 --- a/builder/virtualbox/iso/step_create_disk.go +++ b/builder/virtualbox/iso/step_create_disk.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "path/filepath" "strconv" @@ -15,7 +16,7 @@ type stepCreateDisk struct{} func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_create_vm.go b/builder/virtualbox/iso/step_create_vm.go index 4b88ac8ce..942d33363 100644 --- a/builder/virtualbox/iso/step_create_vm.go +++ b/builder/virtualbox/iso/step_create_vm.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" ) @@ -16,7 +17,7 @@ type stepCreateVM struct { func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) name := config.VMName @@ -60,7 +61,7 @@ func (s *stepCreateVM) Cleanup(state multistep.StateBag) { return } - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) ui.Say("Unregistering and deleting virtual machine...") diff --git a/builder/virtualbox/iso/step_download_guest_additions.go b/builder/virtualbox/iso/step_download_guest_additions.go index 5905dfed7..dbb8b9d3c 100644 --- a/builder/virtualbox/iso/step_download_guest_additions.go +++ b/builder/virtualbox/iso/step_download_guest_additions.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "io" @@ -31,7 +32,7 @@ type stepDownloadGuestAdditions struct{} func (s *stepDownloadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction { var action multistep.StepAction - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) config := state.Get("config").(*config) diff --git a/builder/virtualbox/iso/step_export.go b/builder/virtualbox/iso/step_export.go index 37d113410..5e5f5e8f6 100644 --- a/builder/virtualbox/iso/step_export.go +++ b/builder/virtualbox/iso/step_export.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "path/filepath" @@ -19,7 +20,7 @@ type stepExport struct{} func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_forward_ssh.go b/builder/virtualbox/iso/step_forward_ssh.go index 351513a11..38fd28a35 100644 --- a/builder/virtualbox/iso/step_forward_ssh.go +++ b/builder/virtualbox/iso/step_forward_ssh.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "math/rand" @@ -19,7 +20,7 @@ type stepForwardSSH struct{} func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_remove_devices.go b/builder/virtualbox/iso/step_remove_devices.go index 4fcaeb6e4..763e22670 100644 --- a/builder/virtualbox/iso/step_remove_devices.go +++ b/builder/virtualbox/iso/step_remove_devices.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" ) @@ -15,7 +16,7 @@ import ( type stepRemoveDevices struct{} func (s *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_run.go b/builder/virtualbox/iso/step_run.go index de4936dd9..1d5cc63be 100644 --- a/builder/virtualbox/iso/step_run.go +++ b/builder/virtualbox/iso/step_run.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "time" ) @@ -18,7 +19,7 @@ type stepRun struct { func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -64,7 +65,7 @@ func (s *stepRun) Cleanup(state multistep.StateBag) { return } - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) if running, _ := driver.IsRunning(s.vmName); running { diff --git a/builder/virtualbox/iso/step_shutdown.go b/builder/virtualbox/iso/step_shutdown.go index 7c36a2534..e1f9d5028 100644 --- a/builder/virtualbox/iso/step_shutdown.go +++ b/builder/virtualbox/iso/step_shutdown.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "time" @@ -26,7 +27,7 @@ type stepShutdown struct{} func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { comm := state.Get("communicator").(packer.Communicator) config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_suppress_messages.go b/builder/virtualbox/iso/step_suppress_messages.go index 595d97004..e93397ea7 100644 --- a/builder/virtualbox/iso/step_suppress_messages.go +++ b/builder/virtualbox/iso/step_suppress_messages.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" ) @@ -12,7 +13,7 @@ import ( type stepSuppressMessages struct{} func (stepSuppressMessages) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) log.Println("Suppressing annoying messages in VirtualBox") diff --git a/builder/virtualbox/iso/step_type_boot_command.go b/builder/virtualbox/iso/step_type_boot_command.go index f0680efe9..d479b84e0 100644 --- a/builder/virtualbox/iso/step_type_boot_command.go +++ b/builder/virtualbox/iso/step_type_boot_command.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "strings" @@ -34,7 +35,7 @@ type stepTypeBootCommand struct{} func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) httpPort := state.Get("http_port").(uint) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) diff --git a/builder/virtualbox/iso/step_upload_guest_additions.go b/builder/virtualbox/iso/step_upload_guest_additions.go index 839943125..0ae72c000 100644 --- a/builder/virtualbox/iso/step_upload_guest_additions.go +++ b/builder/virtualbox/iso/step_upload_guest_additions.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "os" @@ -18,7 +19,7 @@ type stepUploadGuestAdditions struct{} func (s *stepUploadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction { comm := state.Get("communicator").(packer.Communicator) config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) // If we're attaching then don't do this, since we attached. diff --git a/builder/virtualbox/iso/step_upload_version.go b/builder/virtualbox/iso/step_upload_version.go index b84b5d948..3517d4f82 100644 --- a/builder/virtualbox/iso/step_upload_version.go +++ b/builder/virtualbox/iso/step_upload_version.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" ) @@ -15,7 +16,7 @@ type stepUploadVersion struct{} func (s *stepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { comm := state.Get("communicator").(packer.Communicator) config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) if config.VBoxVersionFile == "" { diff --git a/builder/virtualbox/iso/step_vboxmanage.go b/builder/virtualbox/iso/step_vboxmanage.go index e5c80f4b5..62b8c0340 100644 --- a/builder/virtualbox/iso/step_vboxmanage.go +++ b/builder/virtualbox/iso/step_vboxmanage.go @@ -3,6 +3,7 @@ package iso import ( "fmt" "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "strings" ) @@ -21,7 +22,7 @@ type stepVBoxManage struct{} func (s *stepVBoxManage) Run(state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*config) - driver := state.Get("driver").(Driver) + driver := state.Get("driver").(vboxcommon.Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) From db167c5a3de6365446ab6241273c86a3c206cb98 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 15:20:15 -0800 Subject: [PATCH 06/30] builder/virtualbox/common: StepOutputDir --- .../step_output_dir.go} | 26 +++-- .../virtualbox/common/step_output_dir_test.go | 96 ++++++++++++++++ builder/virtualbox/common/step_test.go | 17 +++ builder/virtualbox/iso/builder.go | 5 +- builder/virtualbox/ovf/builder.go | 103 ++++++++++++++++++ 5 files changed, 235 insertions(+), 12 deletions(-) rename builder/virtualbox/{iso/step_prepare_output_dir.go => common/step_output_dir.go} (60%) create mode 100644 builder/virtualbox/common/step_output_dir_test.go create mode 100644 builder/virtualbox/common/step_test.go create mode 100644 builder/virtualbox/ovf/builder.go diff --git a/builder/virtualbox/iso/step_prepare_output_dir.go b/builder/virtualbox/common/step_output_dir.go similarity index 60% rename from builder/virtualbox/iso/step_prepare_output_dir.go rename to builder/virtualbox/common/step_output_dir.go index 42c9124b5..209bbabe2 100644 --- a/builder/virtualbox/iso/step_prepare_output_dir.go +++ b/builder/virtualbox/common/step_output_dir.go @@ -1,4 +1,4 @@ -package iso +package common import ( "fmt" @@ -11,25 +11,30 @@ import ( "github.com/mitchellh/packer/packer" ) -type stepPrepareOutputDir struct{} +// StepOutputDir sets up the output directory by creating it if it does +// not exist, deleting it if it does exist and we're forcing, and cleaning +// it up when we're done with it. +type StepOutputDir struct { + Force bool + Path string +} -func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) +func (s *StepOutputDir) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce { + if _, err := os.Stat(s.Path); err == nil && s.Force { ui.Say("Deleting previous output directory...") - os.RemoveAll(config.OutputDir) + os.RemoveAll(s.Path) } // Create the directory - if err := os.MkdirAll(config.OutputDir, 0755); err != nil { + if err := os.MkdirAll(s.Path, 0755); err != nil { state.Put("error", err) return multistep.ActionHalt } // Make sure we can write in the directory - f, err := os.Create(filepath.Join(config.OutputDir, "_packer_perm_check")) + f, err := os.Create(filepath.Join(s.Path, "_packer_perm_check")) if err != nil { err = fmt.Errorf("Couldn't write to output directory: %s", err) state.Put("error", err) @@ -41,17 +46,16 @@ func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) { +func (s *StepOutputDir) Cleanup(state multistep.StateBag) { _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) if cancelled || halted { - config := state.Get("config").(*config) ui := state.Get("ui").(packer.Ui) ui.Say("Deleting output directory...") for i := 0; i < 5; i++ { - err := os.RemoveAll(config.OutputDir) + err := os.RemoveAll(s.Path) if err == nil { break } diff --git a/builder/virtualbox/common/step_output_dir_test.go b/builder/virtualbox/common/step_output_dir_test.go new file mode 100644 index 000000000..be485c278 --- /dev/null +++ b/builder/virtualbox/common/step_output_dir_test.go @@ -0,0 +1,96 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "io/ioutil" + "os" + "testing" +) + +func testStepOutputDir(t *testing.T) *StepOutputDir { + td, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + if err := os.RemoveAll(td); err != nil { + t.Fatalf("err: %s", err) + } + + return &StepOutputDir{Force: false, Path: td} +} + +func TestStepOutputDir_impl(t *testing.T) { + var _ multistep.Step = new(StepOutputDir) +} + +func TestStepOutputDir(t *testing.T) { + state := testState(t) + step := testStepOutputDir(t) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + if _, err := os.Stat(step.Path); err != nil { + t.Fatalf("err: %s", err) + } + + // Test the cleanup + step.Cleanup(state) + if _, err := os.Stat(step.Path); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestStepOutputDir_cancelled(t *testing.T) { + state := testState(t) + step := testStepOutputDir(t) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + if _, err := os.Stat(step.Path); err != nil { + t.Fatalf("err: %s", err) + } + + // Mark + state.Put(multistep.StateCancelled, true) + + // Test the cleanup + step.Cleanup(state) + if _, err := os.Stat(step.Path); err == nil { + t.Fatal("should not exist") + } +} + +func TestStepOutputDir_halted(t *testing.T) { + state := testState(t) + step := testStepOutputDir(t) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + if _, err := os.Stat(step.Path); err != nil { + t.Fatalf("err: %s", err) + } + + // Mark + state.Put(multistep.StateHalted, true) + + // Test the cleanup + step.Cleanup(state) + if _, err := os.Stat(step.Path); err == nil { + t.Fatal("should not exist") + } +} diff --git a/builder/virtualbox/common/step_test.go b/builder/virtualbox/common/step_test.go new file mode 100644 index 000000000..27c37495e --- /dev/null +++ b/builder/virtualbox/common/step_test.go @@ -0,0 +1,17 @@ +package common + +import ( + "bytes" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "testing" +) + +func testState(t *testing.T) multistep.StateBag { + state := new(multistep.BasicStateBag) + state.Put("ui", &packer.BasicUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + }) + return state +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index d2c9ff4b2..725c21783 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -393,7 +393,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ResultKey: "iso_path", Url: b.config.ISOUrls, }, - new(stepPrepareOutputDir), + &vboxcommon.StepOutputDir{ + Force: b.config.PackerForce, + Path: b.config.OutputDir, + }, &common.StepCreateFloppy{ Files: b.config.FloppyFiles, }, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go new file mode 100644 index 000000000..2afcc4361 --- /dev/null +++ b/builder/virtualbox/ovf/builder.go @@ -0,0 +1,103 @@ +package ovf + +import ( + "github.com/mitchellh/packer/builder/virtualbox/common" +) + +// Builder implements packer.Builder and builds the actual VirtualBox +// images. +type Builder struct { + config *Config + runner multistep.Runner +} + +// Prepare processes the build configuration parameters. +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + c, warnings, errs := NewConfig(raws...) + if errs != nil { + return warnings, errs + } + b.config = c + + return warnings, nil +} + +// Run executes a Packer build and returns a packer.Artifact representing +// a VirtualBox appliance. +func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + // Set up the state. + state := new(multistep.BasicStateBag) + state.Put("config", b.config) + state.Put("hook", hook) + state.Put("ui", ui) + + // Build the steps. + steps := []multistep.Step{ + /* + new(stepDownloadGuestAdditions), + */ + /* + new(stepPrepareOutputDir), + &common.StepCreateFloppy{ + Files: b.config.FloppyFiles, + }, + new(stepSuppressMessages), + new(stepAttachGuestAdditions), + new(stepAttachFloppy), + new(stepForwardSSH), + new(stepVBoxManage), + new(stepRun), + new(stepTypeBootCommand), + &common.StepConnectSSH{ + SSHAddress: sshAddress, + SSHConfig: sshConfig, + SSHWaitTimeout: b.config.sshWaitTimeout, + }, + new(stepUploadVersion), + new(stepUploadGuestAdditions), + new(common.StepProvision), + new(stepShutdown), + new(stepRemoveDevices), + new(stepExport), + */ + } + + // Run the steps. + if b.config.PackerDebug { + b.runner = &multistep.DebugRunner{ + Steps: steps, + PauseFn: common.MultistepDebugFn(ui), + } + } else { + b.runner = &multistep.BasicRunner{Steps: steps} + } + b.runner.Run(state) + + // Report any errors. + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + // If we were interrupted or cancelled, then just exit. + if _, ok := state.GetOk(multistep.StateCancelled); ok { + return nil, errors.New("Build was cancelled.") + } + + if _, ok := state.GetOk(multistep.StateHalted); ok { + return nil, errors.New("Build was halted.") + } + + artifact := &Artifact{ + imageName: state.Get("image_name").(string), + driver: driver, + } + return artifact, nil +} + +// Cancel. +func (b *Builder) Cancel() { + if b.runner != nil { + log.Println("Cancelling the step runner...") + b.runner.Cancel() + } +} From 886c0d3ad51825529836741cd4aa229c75e98831 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 16:04:41 -0800 Subject: [PATCH 07/30] builder/virtualbox/common: StepSuppressMessages --- builder/virtualbox/common/driver_mock.go | 65 +++++++++++++++++++ .../{iso => common}/step_suppress_messages.go | 11 ++-- .../common/step_suppress_messages_test.go | 50 ++++++++++++++ builder/virtualbox/common/step_test.go | 1 + builder/virtualbox/iso/builder.go | 2 +- builder/virtualbox/ovf/builder.go | 7 +- 6 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 builder/virtualbox/common/driver_mock.go rename builder/virtualbox/{iso => common}/step_suppress_messages.go (66%) create mode 100644 builder/virtualbox/common/step_suppress_messages_test.go diff --git a/builder/virtualbox/common/driver_mock.go b/builder/virtualbox/common/driver_mock.go new file mode 100644 index 000000000..2a0017120 --- /dev/null +++ b/builder/virtualbox/common/driver_mock.go @@ -0,0 +1,65 @@ +package common + +type DriverMock struct { + CreateSATAControllerVM string + CreateSATAControllerController string + CreateSATAControllerErr error + + IsRunningName string + IsRunningReturn bool + IsRunningErr error + + StopName string + StopErr error + + SuppressMessagesCalled bool + SuppressMessagesErr error + + VBoxManageCalled bool + VBoxManageArgs []string + VBoxManageErr error + + VerifyCalled bool + VerifyErr error + + VersionCalled bool + VersionResult string + VersionErr error +} + +func (d *DriverMock) CreateSATAController(vm string, controller string) error { + d.CreateSATAControllerVM = vm + d.CreateSATAControllerController = vm + return d.CreateSATAControllerErr +} + +func (d *DriverMock) IsRunning(name string) (bool, error) { + d.IsRunningName = name + return d.IsRunningReturn, d.IsRunningErr +} + +func (d *DriverMock) Stop(name string) error { + d.StopName = name + return d.StopErr +} + +func (d *DriverMock) SuppressMessages() error { + d.SuppressMessagesCalled = true + return d.SuppressMessagesErr +} + +func (d *DriverMock) VBoxManage(args ...string) error { + d.VBoxManageCalled = true + d.VBoxManageArgs = args + return d.VBoxManageErr +} + +func (d *DriverMock) Verify() error { + d.VerifyCalled = true + return d.VerifyErr +} + +func (d *DriverMock) Version() (string, error) { + d.VersionCalled = true + return d.VersionResult, d.VersionErr +} diff --git a/builder/virtualbox/iso/step_suppress_messages.go b/builder/virtualbox/common/step_suppress_messages.go similarity index 66% rename from builder/virtualbox/iso/step_suppress_messages.go rename to builder/virtualbox/common/step_suppress_messages.go index e93397ea7..a99576f12 100644 --- a/builder/virtualbox/iso/step_suppress_messages.go +++ b/builder/virtualbox/common/step_suppress_messages.go @@ -1,19 +1,18 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" ) // This step sets some variables in VirtualBox so that annoying // pop-up messages don't exist. -type stepSuppressMessages struct{} +type StepSuppressMessages struct{} -func (stepSuppressMessages) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(vboxcommon.Driver) +func (StepSuppressMessages) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) log.Println("Suppressing annoying messages in VirtualBox") @@ -27,4 +26,4 @@ func (stepSuppressMessages) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (stepSuppressMessages) Cleanup(multistep.StateBag) {} +func (StepSuppressMessages) Cleanup(multistep.StateBag) {} diff --git a/builder/virtualbox/common/step_suppress_messages_test.go b/builder/virtualbox/common/step_suppress_messages_test.go new file mode 100644 index 000000000..44612ecbc --- /dev/null +++ b/builder/virtualbox/common/step_suppress_messages_test.go @@ -0,0 +1,50 @@ +package common + +import ( + "errors" + "github.com/mitchellh/multistep" + "testing" +) + +func TestStepSuppressMessages_impl(t *testing.T) { + var _ multistep.Step = new(StepSuppressMessages) +} + +func TestStepSuppressMessages(t *testing.T) { + state := testState(t) + step := new(StepSuppressMessages) + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + if !driver.SuppressMessagesCalled { + t.Fatal("should call suppressmessages") + } +} + +func TestStepSuppressMessages_error(t *testing.T) { + state := testState(t) + step := new(StepSuppressMessages) + + driver := state.Get("driver").(*DriverMock) + driver.SuppressMessagesErr = errors.New("foo") + + // Test the run + if action := step.Run(state); action != multistep.ActionHalt { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); !ok { + t.Fatal("should have error") + } + + if !driver.SuppressMessagesCalled { + t.Fatal("should call suppressmessages") + } +} diff --git a/builder/virtualbox/common/step_test.go b/builder/virtualbox/common/step_test.go index 27c37495e..9bf6d5d67 100644 --- a/builder/virtualbox/common/step_test.go +++ b/builder/virtualbox/common/step_test.go @@ -9,6 +9,7 @@ import ( func testState(t *testing.T) multistep.StateBag { state := new(multistep.BasicStateBag) + state.Put("driver", new(DriverMock)) state.Put("ui", &packer.BasicUi{ Reader: new(bytes.Buffer), Writer: new(bytes.Buffer), diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 725c21783..11a275c90 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -401,7 +401,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Files: b.config.FloppyFiles, }, new(stepHTTPServer), - new(stepSuppressMessages), + new(vboxcommon.StepSuppressMessages), new(stepCreateVM), new(stepCreateDisk), new(stepAttachISO), diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 2afcc4361..5126ef1a9 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -37,7 +37,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepDownloadGuestAdditions), */ /* - new(stepPrepareOutputDir), + &vboxcommon.StepOutputDir{ + Force: b.config.PackerForce, + Path: b.config.OutputDir, + }, + */ + /* &common.StepCreateFloppy{ Files: b.config.FloppyFiles, }, From 598822d46be86dd71d768ddf70434268b076e876 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 21 Dec 2013 17:38:06 -0800 Subject: [PATCH 08/30] builder/virtualbox/common: move outputdir config to common --- builder/virtualbox/common/config_test.go | 15 ++++ builder/virtualbox/common/output_config.go | 40 +++++++++++ .../virtualbox/common/output_config_test.go | 65 ++++++++++++++++++ builder/virtualbox/iso/builder.go | 19 ++---- builder/virtualbox/ovf/builder.go | 68 +++++++++---------- builder/virtualbox/ovf/config.go | 40 +++++++++++ builder/virtualbox/ovf/config_test.go | 9 +++ 7 files changed, 207 insertions(+), 49 deletions(-) create mode 100644 builder/virtualbox/common/config_test.go create mode 100644 builder/virtualbox/common/output_config.go create mode 100644 builder/virtualbox/common/output_config_test.go create mode 100644 builder/virtualbox/ovf/config.go create mode 100644 builder/virtualbox/ovf/config_test.go diff --git a/builder/virtualbox/common/config_test.go b/builder/virtualbox/common/config_test.go new file mode 100644 index 000000000..a84c51bc1 --- /dev/null +++ b/builder/virtualbox/common/config_test.go @@ -0,0 +1,15 @@ +package common + +import ( + "github.com/mitchellh/packer/packer" + "testing" +) + +func testConfigTemplate(t *testing.T) *packer.ConfigTemplate { + result, err := packer.NewConfigTemplate() + if err != nil { + t.Fatalf("err: %s", err) + } + + return result +} diff --git a/builder/virtualbox/common/output_config.go b/builder/virtualbox/common/output_config.go new file mode 100644 index 000000000..19be1ba00 --- /dev/null +++ b/builder/virtualbox/common/output_config.go @@ -0,0 +1,40 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" + "os" +) + +type OutputConfig struct { + OutputDir string `mapstructure:"output_directory"` +} + +func (c *OutputConfig) Prepare(t *packer.ConfigTemplate, pc *common.PackerConfig) []error { + if c.OutputDir == "" { + c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName) + } + + templates := map[string]*string{ + "output_directory": &c.OutputDir, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + if !pc.PackerForce { + if _, err := os.Stat(c.OutputDir); err == nil { + errs = append(errs, fmt.Errorf( + "Output directory '%s' already exists. It must not exist.", c.OutputDir)) + } + } + + return errs +} diff --git a/builder/virtualbox/common/output_config_test.go b/builder/virtualbox/common/output_config_test.go new file mode 100644 index 000000000..7fa039a16 --- /dev/null +++ b/builder/virtualbox/common/output_config_test.go @@ -0,0 +1,65 @@ +package common + +import ( + "github.com/mitchellh/packer/common" + "io/ioutil" + "os" + "testing" +) + +func TestOutputConfigPrepare(t *testing.T) { + c := new(OutputConfig) + if c.OutputDir != "" { + t.Fatalf("what: %s", c.OutputDir) + } + + pc := &common.PackerConfig{PackerBuildName: "foo"} + errs := c.Prepare(testConfigTemplate(t), pc) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + if c.OutputDir == "" { + t.Fatal("should have output dir") + } +} + +func TestOutputConfigPrepare_exists(t *testing.T) { + td, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(td) + + c := new(OutputConfig) + c.OutputDir = td + + pc := &common.PackerConfig{ + PackerBuildName: "foo", + PackerForce: false, + } + errs := c.Prepare(testConfigTemplate(t), pc) + if len(errs) == 0 { + t.Fatal("should have errors") + } +} + +func TestOutputConfigPrepare_forceExists(t *testing.T) { + td, err := ioutil.TempDir("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.RemoveAll(td) + + c := new(OutputConfig) + c.OutputDir = td + + pc := &common.PackerConfig{ + PackerBuildName: "foo", + PackerForce: true, + } + errs := c.Prepare(testConfigTemplate(t), pc) + if len(errs) > 0 { + t.Fatal("should not have errors") + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 11a275c90..fddd36b6d 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -29,7 +29,8 @@ type Builder struct { } type config struct { - common.PackerConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` @@ -48,7 +49,6 @@ type config struct { ISOChecksum string `mapstructure:"iso_checksum"` ISOChecksumType string `mapstructure:"iso_checksum_type"` ISOUrls []string `mapstructure:"iso_urls"` - OutputDir string `mapstructure:"output_directory"` ShutdownCommand string `mapstructure:"shutdown_command"` SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` @@ -85,6 +85,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Accumulate any errors and warnings errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend( + errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) warnings := make([]string, 0) if b.config.DiskSize == 0 { @@ -119,10 +121,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.HTTPPortMax = 9000 } - if b.config.OutputDir == "" { - b.config.OutputDir = fmt.Sprintf("output-%s", b.config.PackerBuildName) - } - if b.config.RawBootWait == "" { b.config.RawBootWait = "10s" } @@ -165,7 +163,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { "iso_checksum": &b.config.ISOChecksum, "iso_checksum_type": &b.config.ISOChecksumType, "iso_url": &b.config.RawSingleISOUrl, - "output_directory": &b.config.OutputDir, "shutdown_command": &b.config.ShutdownCommand, "ssh_key_path": &b.config.SSHKeyPath, "ssh_password": &b.config.SSHPassword, @@ -300,14 +297,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256) } - if !b.config.PackerForce { - if _, err := os.Stat(b.config.OutputDir); err == nil { - errs = packer.MultiErrorAppend( - errs, - fmt.Errorf("Output directory '%s' already exists. It must not exist.", b.config.OutputDir)) - } - } - b.config.bootWait, err = time.ParseDuration(b.config.RawBootWait) if err != nil { errs = packer.MultiErrorAppend( diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 5126ef1a9..18195644a 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -1,7 +1,13 @@ package ovf import ( - "github.com/mitchellh/packer/builder/virtualbox/common" + "errors" + "log" + + "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" ) // Builder implements packer.Builder and builds the actual VirtualBox @@ -33,38 +39,36 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{ - /* - new(stepDownloadGuestAdditions), - */ - /* + /* + new(stepDownloadGuestAdditions), + */ &vboxcommon.StepOutputDir{ Force: b.config.PackerForce, Path: b.config.OutputDir, }, - */ - /* - &common.StepCreateFloppy{ - Files: b.config.FloppyFiles, - }, - new(stepSuppressMessages), - new(stepAttachGuestAdditions), - new(stepAttachFloppy), - new(stepForwardSSH), - new(stepVBoxManage), - new(stepRun), - new(stepTypeBootCommand), - &common.StepConnectSSH{ - SSHAddress: sshAddress, - SSHConfig: sshConfig, - SSHWaitTimeout: b.config.sshWaitTimeout, - }, - new(stepUploadVersion), - new(stepUploadGuestAdditions), - new(common.StepProvision), - new(stepShutdown), - new(stepRemoveDevices), - new(stepExport), - */ + new(vboxcommon.StepSuppressMessages), + /* + &common.StepCreateFloppy{ + Files: b.config.FloppyFiles, + }, + new(stepAttachGuestAdditions), + new(stepAttachFloppy), + new(stepForwardSSH), + new(stepVBoxManage), + new(stepRun), + new(stepTypeBootCommand), + &common.StepConnectSSH{ + SSHAddress: sshAddress, + SSHConfig: sshConfig, + SSHWaitTimeout: b.config.sshWaitTimeout, + }, + new(stepUploadVersion), + new(stepUploadGuestAdditions), + new(common.StepProvision), + new(stepShutdown), + new(stepRemoveDevices), + new(stepExport), + */ } // Run the steps. @@ -92,11 +96,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, errors.New("Build was halted.") } - artifact := &Artifact{ - imageName: state.Get("image_name").(string), - driver: driver, - } - return artifact, nil + return vboxcommon.NewArtifact(b.config.OutputDir) } // Cancel. diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go new file mode 100644 index 000000000..3064365e6 --- /dev/null +++ b/builder/virtualbox/ovf/config.go @@ -0,0 +1,40 @@ +package ovf + +import ( + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" + "github.com/mitchellh/packer/common" + "github.com/mitchellh/packer/packer" +) + +// Config is the configuration structure for the builder. +type Config struct { + common.PackerConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` + + tpl *packer.ConfigTemplate +} + +func NewConfig(raws ...interface{}) (*Config, []string, error) { + c := new(Config) + md, err := common.DecodeConfig(c, raws...) + if err != nil { + return nil, nil, err + } + + c.tpl, err = packer.NewConfigTemplate() + if err != nil { + return nil, nil, err + } + c.tpl.UserVars = c.PackerUserVars + + // Prepare the errors + errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(c.tpl, &c.PackerConfig)...) + + // Check for any errors. + if errs != nil && len(errs.Errors) > 0 { + return nil, nil, errs + } + + return c, nil, nil +} diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go new file mode 100644 index 000000000..0981344e0 --- /dev/null +++ b/builder/virtualbox/ovf/config_test.go @@ -0,0 +1,9 @@ +package ovf + +import ( + "testing" +) + +func testConfig(t *testing.T) map[string]interface{} { + return map[string]interface{}{} +} From d731dcd8f752705d07f0f6fa2d765750625cc072 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 07:58:07 -0800 Subject: [PATCH 09/30] builder/virtualbox: floppy files config --- builder/virtualbox/common/floppy_config.go | 31 +++++++++++++++++++ .../virtualbox/common/floppy_config_test.go | 18 +++++++++++ builder/virtualbox/iso/builder.go | 16 +--------- builder/virtualbox/ovf/builder.go | 6 ++-- builder/virtualbox/ovf/config.go | 1 + 5 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 builder/virtualbox/common/floppy_config.go create mode 100644 builder/virtualbox/common/floppy_config_test.go diff --git a/builder/virtualbox/common/floppy_config.go b/builder/virtualbox/common/floppy_config.go new file mode 100644 index 000000000..35cd7aca4 --- /dev/null +++ b/builder/virtualbox/common/floppy_config.go @@ -0,0 +1,31 @@ +package common + +import ( + "fmt" + + "github.com/mitchellh/packer/packer" +) + +// FloppyConfig is configuration related to created floppy disks and attaching +// them to a VirtualBox machine. +type FloppyConfig struct { + FloppyFiles []string `mapstructure:"floppy_files"` +} + +func (c *FloppyConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.FloppyFiles == nil { + c.FloppyFiles = make([]string, 0) + } + + errs := make([]error, 0) + for i, file := range c.FloppyFiles { + var err error + c.FloppyFiles[i], err = t.Process(file, nil) + if err != nil { + errs = append(errs, fmt.Errorf( + "Error processing floppy_files[%d]: %s", i, err)) + } + } + + return errs +} diff --git a/builder/virtualbox/common/floppy_config_test.go b/builder/virtualbox/common/floppy_config_test.go new file mode 100644 index 000000000..3e4fc55db --- /dev/null +++ b/builder/virtualbox/common/floppy_config_test.go @@ -0,0 +1,18 @@ +package common + +import ( + "testing" +) + +func TestFloppyConfigPrepare(t *testing.T) { + c := new(FloppyConfig) + + errs := c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + if len(c.FloppyFiles) > 0 { + t.Fatal("should not have floppy files") + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index fddd36b6d..f4948cb04 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -30,11 +30,11 @@ type Builder struct { type config struct { common.PackerConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` - FloppyFiles []string `mapstructure:"floppy_files"` Format string `mapstructure:"format"` GuestAdditionsMode string `mapstructure:"guest_additions_mode"` GuestAdditionsPath string `mapstructure:"guest_additions_path"` @@ -93,10 +93,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.DiskSize = 40000 } - if b.config.FloppyFiles == nil { - b.config.FloppyFiles = make([]string, 0) - } - if b.config.GuestAdditionsMode == "" { b.config.GuestAdditionsMode = "upload" } @@ -212,16 +208,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } - for i, file := range b.config.FloppyFiles { - var err error - b.config.FloppyFiles[i], err = b.config.tpl.Process(file, nil) - if err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Error processing floppy_files[%d]: %s", - i, err)) - } - } - if !(b.config.Format == "ovf" || b.config.Format == "ova") { errs = packer.MultiErrorAppend( errs, errors.New("invalid format, only 'ovf' or 'ova' are allowed")) diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 18195644a..1d7c088d9 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -47,10 +47,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Path: b.config.OutputDir, }, new(vboxcommon.StepSuppressMessages), + &common.StepCreateFloppy{ + Files: b.config.FloppyFiles, + }, /* - &common.StepCreateFloppy{ - Files: b.config.FloppyFiles, - }, new(stepAttachGuestAdditions), new(stepAttachFloppy), new(stepForwardSSH), diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 3064365e6..7dcf0da50 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -9,6 +9,7 @@ import ( // Config is the configuration structure for the builder. type Config struct { common.PackerConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate From 79c0c6b5457dd7c8643480548588c9e300cce0de Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 08:10:11 -0800 Subject: [PATCH 10/30] builder/virtualbox: step attach floppy --- builder/virtualbox/common/driver_mock.go | 14 ++-- .../{iso => common}/step_attach_floppy.go | 18 +++-- .../common/step_attach_floppy_test.go | 73 +++++++++++++++++++ builder/virtualbox/iso/builder.go | 2 +- builder/virtualbox/ovf/builder.go | 4 +- 5 files changed, 95 insertions(+), 16 deletions(-) rename builder/virtualbox/{iso => common}/step_attach_floppy.go (86%) create mode 100644 builder/virtualbox/common/step_attach_floppy_test.go diff --git a/builder/virtualbox/common/driver_mock.go b/builder/virtualbox/common/driver_mock.go index 2a0017120..ad00ae248 100644 --- a/builder/virtualbox/common/driver_mock.go +++ b/builder/virtualbox/common/driver_mock.go @@ -15,9 +15,8 @@ type DriverMock struct { SuppressMessagesCalled bool SuppressMessagesErr error - VBoxManageCalled bool - VBoxManageArgs []string - VBoxManageErr error + VBoxManageCalls [][]string + VBoxManageErrs []error VerifyCalled bool VerifyErr error @@ -49,9 +48,12 @@ func (d *DriverMock) SuppressMessages() error { } func (d *DriverMock) VBoxManage(args ...string) error { - d.VBoxManageCalled = true - d.VBoxManageArgs = args - return d.VBoxManageErr + d.VBoxManageCalls = append(d.VBoxManageCalls, args) + + if len(d.VBoxManageErrs) >= len(d.VBoxManageCalls) { + return d.VBoxManageErrs[len(d.VBoxManageCalls)-1] + } + return nil } func (d *DriverMock) Verify() error { diff --git a/builder/virtualbox/iso/step_attach_floppy.go b/builder/virtualbox/common/step_attach_floppy.go similarity index 86% rename from builder/virtualbox/iso/step_attach_floppy.go rename to builder/virtualbox/common/step_attach_floppy.go index e784a0fc0..5ebcf9b5f 100644 --- a/builder/virtualbox/iso/step_attach_floppy.go +++ b/builder/virtualbox/common/step_attach_floppy.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "io" "io/ioutil" @@ -15,13 +14,16 @@ import ( // This step attaches the ISO to the virtual machine. // // Uses: +// driver Driver +// ui packer.Ui +// vmName string // // Produces: -type stepAttachFloppy struct { +type StepAttachFloppy struct { floppyPath string } -func (s *stepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction { +func (s *StepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction { // Determine if we even have a floppy disk to attach var floppyPath string if floppyPathRaw, ok := state.GetOk("floppy_path"); ok { @@ -40,7 +42,7 @@ func (s *stepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - driver := state.Get("driver").(vboxcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -77,7 +79,7 @@ func (s *stepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepAttachFloppy) Cleanup(state multistep.StateBag) { +func (s *StepAttachFloppy) Cleanup(state multistep.StateBag) { if s.floppyPath == "" { return } @@ -85,7 +87,7 @@ func (s *stepAttachFloppy) Cleanup(state multistep.StateBag) { // Delete the floppy disk defer os.Remove(s.floppyPath) - driver := state.Get("driver").(vboxcommon.Driver) + driver := state.Get("driver").(Driver) vmName := state.Get("vmName").(string) command := []string{ @@ -101,7 +103,7 @@ func (s *stepAttachFloppy) Cleanup(state multistep.StateBag) { } } -func (s *stepAttachFloppy) copyFloppy(path string) (string, error) { +func (s *StepAttachFloppy) copyFloppy(path string) (string, error) { tempdir, err := ioutil.TempDir("", "packer") if err != nil { return "", err diff --git a/builder/virtualbox/common/step_attach_floppy_test.go b/builder/virtualbox/common/step_attach_floppy_test.go new file mode 100644 index 000000000..110ddfe74 --- /dev/null +++ b/builder/virtualbox/common/step_attach_floppy_test.go @@ -0,0 +1,73 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "io/ioutil" + "os" + "testing" +) + +func TestStepAttachFloppy_impl(t *testing.T) { + var _ multistep.Step = new(StepAttachFloppy) +} + +func TestStepAttachFloppy(t *testing.T) { + state := testState(t) + step := new(StepAttachFloppy) + + // Create a temporary file for our floppy file + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + tf.Close() + defer os.Remove(tf.Name()) + + state.Put("floppy_path", tf.Name()) + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + if len(driver.VBoxManageCalls) != 2 { + t.Fatal("not enough calls to VBoxManage") + } + if driver.VBoxManageCalls[0][0] != "storagectl" { + t.Fatal("bad call") + } + if driver.VBoxManageCalls[1][0] != "storageattach" { + t.Fatal("bad call") + } + + // Test the cleanup + step.Cleanup(state) + if driver.VBoxManageCalls[2][0] != "storageattach" { + t.Fatal("bad call") + } +} + +func TestStepAttachFloppy_noFloppy(t *testing.T) { + state := testState(t) + step := new(StepAttachFloppy) + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + if len(driver.VBoxManageCalls) > 0 { + t.Fatal("should not call vboxmanage") + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index f4948cb04..353cfdcb1 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -381,7 +381,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepCreateDisk), new(stepAttachISO), new(stepAttachGuestAdditions), - new(stepAttachFloppy), + new(vboxcommon.StepAttachFloppy), new(stepForwardSSH), new(stepVBoxManage), new(stepRun), diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 1d7c088d9..831fb3214 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -52,7 +52,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, /* new(stepAttachGuestAdditions), - new(stepAttachFloppy), + */ + new(vboxcommon.StepAttachFloppy), + /* new(stepForwardSSH), new(stepVBoxManage), new(stepRun), From 7a4ff3f2b114988b1faab7541b179048480940cc Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 09:08:09 -0800 Subject: [PATCH 11/30] builder/virtualbox: generic SSHConfig --- builder/virtualbox/common/ssh.go | 59 ++++ builder/virtualbox/common/ssh_config.go | 80 +++++ builder/virtualbox/common/ssh_config_test.go | 186 ++++++++++++ .../{iso => common}/step_forward_ssh.go | 30 +- builder/virtualbox/iso/builder.go | 70 +---- builder/virtualbox/iso/builder_test.go | 279 ------------------ builder/virtualbox/iso/ssh.go | 59 ---- builder/virtualbox/ovf/builder.go | 19 +- builder/virtualbox/ovf/config.go | 3 + 9 files changed, 369 insertions(+), 416 deletions(-) create mode 100644 builder/virtualbox/common/ssh.go create mode 100644 builder/virtualbox/common/ssh_config.go create mode 100644 builder/virtualbox/common/ssh_config_test.go rename builder/virtualbox/{iso => common}/step_forward_ssh.go (62%) delete mode 100644 builder/virtualbox/iso/ssh.go diff --git a/builder/virtualbox/common/ssh.go b/builder/virtualbox/common/ssh.go new file mode 100644 index 000000000..79f6c46f3 --- /dev/null +++ b/builder/virtualbox/common/ssh.go @@ -0,0 +1,59 @@ +package common + +import ( + gossh "code.google.com/p/go.crypto/ssh" + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/communicator/ssh" + "io/ioutil" + "os" +) + +func SSHAddress(state multistep.StateBag) (string, error) { + sshHostPort := state.Get("sshHostPort").(uint) + return fmt.Sprintf("127.0.0.1:%d", sshHostPort), nil +} + +func SSHConfigFunc(config SSHConfig) func(multistep.StateBag) (*gossh.ClientConfig, error) { + return func(state multistep.StateBag) (*gossh.ClientConfig, error) { + auth := []gossh.ClientAuth{ + gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)), + gossh.ClientAuthKeyboardInteractive( + ssh.PasswordKeyboardInteractive(config.SSHPassword)), + } + + if config.SSHKeyPath != "" { + keyring, err := sshKeyToKeyring(config.SSHKeyPath) + if err != nil { + return nil, err + } + + auth = append(auth, gossh.ClientAuthKeyring(keyring)) + } + + return &gossh.ClientConfig{ + User: config.SSHUser, + Auth: auth, + }, nil + } +} + +func sshKeyToKeyring(path string) (gossh.ClientKeyring, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + keyBytes, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + keyring := new(ssh.SimpleKeychain) + if err := keyring.AddPEMKey(string(keyBytes)); err != nil { + return nil, err + } + + return keyring, nil +} diff --git a/builder/virtualbox/common/ssh_config.go b/builder/virtualbox/common/ssh_config.go new file mode 100644 index 000000000..c73cb0386 --- /dev/null +++ b/builder/virtualbox/common/ssh_config.go @@ -0,0 +1,80 @@ +package common + +import ( + "errors" + "fmt" + "github.com/mitchellh/packer/packer" + "os" + "time" +) + +type SSHConfig struct { + SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` + SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` + SSHKeyPath string `mapstructure:"ssh_key_path"` + SSHPassword string `mapstructure:"ssh_password"` + SSHPort uint `mapstructure:"ssh_port"` + SSHUser string `mapstructure:"ssh_username"` + RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"` + + SSHWaitTimeout time.Duration +} + +func (c *SSHConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.SSHHostPortMin == 0 { + c.SSHHostPortMin = 2222 + } + + if c.SSHHostPortMax == 0 { + c.SSHHostPortMax = 4444 + } + + if c.SSHPort == 0 { + c.SSHPort = 22 + } + + if c.RawSSHWaitTimeout == "" { + c.RawSSHWaitTimeout = "20m" + } + + templates := map[string]*string{ + "ssh_key_path": &c.SSHKeyPath, + "ssh_password": &c.SSHPassword, + "ssh_username": &c.SSHUser, + "ssh_wait_timeout": &c.RawSSHWaitTimeout, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + if c.SSHKeyPath != "" { + if _, err := os.Stat(c.SSHKeyPath); err != nil { + errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err)) + } else if _, err := sshKeyToKeyring(c.SSHKeyPath); err != nil { + errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err)) + } + } + + if c.SSHHostPortMin > c.SSHHostPortMax { + errs = append(errs, + errors.New("ssh_host_port_min must be less than ssh_host_port_max")) + } + + if c.SSHUser == "" { + errs = append(errs, errors.New("An ssh_username must be specified.")) + } + + var err error + c.SSHWaitTimeout, err = time.ParseDuration(c.RawSSHWaitTimeout) + if err != nil { + errs = append(errs, fmt.Errorf("Failed parsing ssh_wait_timeout: %s", err)) + } + + return errs +} diff --git a/builder/virtualbox/common/ssh_config_test.go b/builder/virtualbox/common/ssh_config_test.go new file mode 100644 index 000000000..489b7eae8 --- /dev/null +++ b/builder/virtualbox/common/ssh_config_test.go @@ -0,0 +1,186 @@ +package common + +import ( + "io/ioutil" + "os" + "testing" +) + +func testSSHConfig() *SSHConfig { + return &SSHConfig{ + SSHUser: "foo", + } +} + +func TestSSHConfigPrepare(t *testing.T) { + c := testSSHConfig() + errs := c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + if c.SSHHostPortMin != 2222 { + t.Errorf("bad min ssh host port: %d", c.SSHHostPortMin) + } + + if c.SSHHostPortMax != 4444 { + t.Errorf("bad max ssh host port: %d", c.SSHHostPortMax) + } + + if c.SSHPort != 22 { + t.Errorf("bad ssh port: %d", c.SSHPort) + } +} + +func TestSSHConfigPrepare_SSHHostPort(t *testing.T) { + var c *SSHConfig + var errs []error + + // Bad + c = testSSHConfig() + c.SSHHostPortMin = 1000 + c.SSHHostPortMax = 500 + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("bad: %#v", errs) + } + + // Good + c = testSSHConfig() + c.SSHHostPortMin = 50 + c.SSHHostPortMax = 500 + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } +} + +func TestSSHConfigPrepare_SSHKeyPath(t *testing.T) { + var c *SSHConfig + var errs []error + + c = testSSHConfig() + c.SSHKeyPath = "" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %#v", errs) + } + + c = testSSHConfig() + c.SSHKeyPath = "/i/dont/exist" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatal("should have error") + } + + // Test bad contents + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(tf.Name()) + defer tf.Close() + + if _, err := tf.Write([]byte("HELLO!")); err != nil { + t.Fatalf("err: %s", err) + } + + c = testSSHConfig() + c.SSHKeyPath = tf.Name() + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatal("should have error") + } + + // Test good contents + tf.Seek(0, 0) + tf.Truncate(0) + tf.Write([]byte(testPem)) + c = testSSHConfig() + c.SSHKeyPath = tf.Name() + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %#v", errs) + } +} + +func TestSSHConfigPrepare_SSHUser(t *testing.T) { + var c *SSHConfig + var errs []error + + c = testSSHConfig() + c.SSHUser = "" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("should have error") + } + + c = testSSHConfig() + c.SSHUser = "exists" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %#v", errs) + } +} + +func TestSSHConfigPrepare_SSHWaitTimeout(t *testing.T) { + var c *SSHConfig + var errs []error + + // Defaults + c = testSSHConfig() + c.RawSSHWaitTimeout = "" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %#v", errs) + } + if c.RawSSHWaitTimeout != "20m" { + t.Fatalf("bad value: %s", c.RawSSHWaitTimeout) + } + + // Test with a bad value + c = testSSHConfig() + c.RawSSHWaitTimeout = "this is not good" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatal("should have error") + } + + // Test with a good one + c = testSSHConfig() + c.RawSSHWaitTimeout = "5s" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %#v", errs) + } +} + +const testPem = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAxd4iamvrwRJvtNDGQSIbNvvIQN8imXTRWlRY62EvKov60vqu +hh+rDzFYAIIzlmrJopvOe0clqmi3mIP9dtkjPFrYflq52a2CF5q+BdwsJXuRHbJW +LmStZUwW1khSz93DhvhmK50nIaczW63u4EO/jJb3xj+wxR1Nkk9bxi3DDsYFt8SN +AzYx9kjlEYQ/+sI4/ATfmdV9h78SVotjScupd9KFzzi76gWq9gwyCBLRynTUWlyD +2UOfJRkOvhN6/jKzvYfVVwjPSfA9IMuooHdScmC4F6KBKJl/zf/zETM0XyzIDNmH +uOPbCiljq2WoRM+rY6ET84EO0kVXbfx8uxUsqQIDAQABAoIBAQCkPj9TF0IagbM3 +5BSs/CKbAWS4dH/D4bPlxx4IRCNirc8GUg+MRb04Xz0tLuajdQDqeWpr6iLZ0RKV +BvreLF+TOdV7DNQ4XE4gSdJyCtCaTHeort/aordL3l0WgfI7mVk0L/yfN1PEG4YG +E9q1TYcyrB3/8d5JwIkjabxERLglCcP+geOEJp+QijbvFIaZR/n2irlKW4gSy6ko +9B0fgUnhkHysSg49ChHQBPQ+o5BbpuLrPDFMiTPTPhdfsvGGcyCGeqfBA56oHcSF +K02Fg8OM+Bd1lb48LAN9nWWY4WbwV+9bkN3Ym8hO4c3a/Dxf2N7LtAQqWZzFjvM3 +/AaDvAgBAoGBAPLD+Xn1IYQPMB2XXCXfOuJewRY7RzoVWvMffJPDfm16O7wOiW5+ +2FmvxUDayk4PZy6wQMzGeGKnhcMMZTyaq2g/QtGfrvy7q1Lw2fB1VFlVblvqhoJa +nMJojjC4zgjBkXMHsRLeTmgUKyGs+fdFbfI6uejBnnf+eMVUMIdJ+6I9AoGBANCn +kWO9640dttyXURxNJ3lBr2H3dJOkmD6XS+u+LWqCSKQe691Y/fZ/ZL0Oc4Mhy7I6 +hsy3kDQ5k2V0fkaNODQIFJvUqXw2pMewUk8hHc9403f4fe9cPrL12rQ8WlQw4yoC +v2B61vNczCCUDtGxlAaw8jzSRaSI5s6ax3K7enbdAoGBAJB1WYDfA2CoAQO6y9Sl +b07A/7kQ8SN5DbPaqrDrBdJziBQxukoMJQXJeGFNUFD/DXFU5Fp2R7C86vXT7HIR +v6m66zH+CYzOx/YE6EsUJms6UP9VIVF0Rg/RU7teXQwM01ZV32LQ8mswhTH20o/3 +uqMHmxUMEhZpUMhrfq0isyApAoGAe1UxGTXfj9AqkIVYylPIq2HqGww7+jFmVEj1 +9Wi6S6Sq72ffnzzFEPkIQL/UA4TsdHMnzsYKFPSbbXLIWUeMGyVTmTDA5c0e5XIR +lPhMOKCAzv8w4VUzMnEkTzkFY5JqFCD/ojW57KvDdNZPVB+VEcdxyAW6aKELXMAc +eHLc1nkCgYEApm/motCTPN32nINZ+Vvywbv64ZD+gtpeMNP3CLrbe1X9O+H52AXa +1jCoOldWR8i2bs2NVPcKZgdo6fFULqE4dBX7Te/uYEIuuZhYLNzRO1IKU/YaqsXG +3bfQ8hKYcSnTfE0gPtLDnqCIxTocaGLSHeG3TH9fTw+dA8FvWpUztI4= +-----END RSA PRIVATE KEY----- +` diff --git a/builder/virtualbox/iso/step_forward_ssh.go b/builder/virtualbox/common/step_forward_ssh.go similarity index 62% rename from builder/virtualbox/iso/step_forward_ssh.go rename to builder/virtualbox/common/step_forward_ssh.go index 38fd28a35..862432952 100644 --- a/builder/virtualbox/iso/step_forward_ssh.go +++ b/builder/virtualbox/common/step_forward_ssh.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "math/rand" @@ -14,30 +13,37 @@ import ( // on the guest machine. // // Uses: +// driver Driver +// ui packer.Ui +// vmName string // // Produces: -type stepForwardSSH struct{} +type StepForwardSSH struct { + GuestPort uint + HostPortMin uint + HostPortMax uint +} -func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepForwardSSH) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) - log.Printf("Looking for available SSH port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax) + log.Printf("Looking for available SSH port between %d and %d", + s.HostPortMin, s.HostPortMax) var sshHostPort uint var offset uint = 0 - portRange := int(config.SSHHostPortMax - config.SSHHostPortMin) + portRange := int(s.HostPortMax - s.HostPortMin) if portRange > 0 { // Have to check if > 0 to avoid a panic offset = uint(rand.Intn(portRange)) } for { - sshHostPort = offset + config.SSHHostPortMin + sshHostPort = offset + s.HostPortMin log.Printf("Trying port: %d", sshHostPort) - l, err := net.Listen("tcp", fmt.Sprintf(":%d", sshHostPort)) + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort)) if err == nil { defer l.Close() break @@ -49,7 +55,7 @@ func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction { command := []string{ "modifyvm", vmName, "--natpf1", - fmt.Sprintf("packerssh,tcp,127.0.0.1,%d,,%d", sshHostPort, config.SSHPort), + fmt.Sprintf("packerssh,tcp,127.0.0.1,%d,,%d", sshHostPort, s.GuestPort), } if err := driver.VBoxManage(command...); err != nil { err := fmt.Errorf("Error creating port forwarding rule: %s", err) @@ -64,4 +70,4 @@ func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepForwardSSH) Cleanup(state multistep.StateBag) {} +func (s *StepForwardSSH) Cleanup(state multistep.StateBag) {} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 353cfdcb1..0a7a21426 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -8,7 +8,6 @@ import ( "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "log" - "os" "strings" "time" ) @@ -32,6 +31,7 @@ type config struct { common.PackerConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` @@ -50,12 +50,6 @@ type config struct { ISOChecksumType string `mapstructure:"iso_checksum_type"` ISOUrls []string `mapstructure:"iso_urls"` ShutdownCommand string `mapstructure:"shutdown_command"` - SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` - SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` - SSHKeyPath string `mapstructure:"ssh_key_path"` - SSHPassword string `mapstructure:"ssh_password"` - SSHPort uint `mapstructure:"ssh_port"` - SSHUser string `mapstructure:"ssh_username"` VBoxVersionFile string `mapstructure:"virtualbox_version_file"` VBoxManage [][]string `mapstructure:"vboxmanage"` VMName string `mapstructure:"vm_name"` @@ -63,11 +57,9 @@ type config struct { RawBootWait string `mapstructure:"boot_wait"` RawSingleISOUrl string `mapstructure:"iso_url"` RawShutdownTimeout string `mapstructure:"shutdown_timeout"` - RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"` bootWait time.Duration `` shutdownTimeout time.Duration `` - sshWaitTimeout time.Duration `` tpl *packer.ConfigTemplate } @@ -85,8 +77,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Accumulate any errors and warnings errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend( errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) warnings := make([]string, 0) if b.config.DiskSize == 0 { @@ -121,18 +115,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.RawBootWait = "10s" } - if b.config.SSHHostPortMin == 0 { - b.config.SSHHostPortMin = 2222 - } - - if b.config.SSHHostPortMax == 0 { - b.config.SSHHostPortMax = 4444 - } - - if b.config.SSHPort == 0 { - b.config.SSHPort = 22 - } - if b.config.VBoxManage == nil { b.config.VBoxManage = make([][]string, 0) } @@ -160,15 +142,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { "iso_checksum_type": &b.config.ISOChecksumType, "iso_url": &b.config.RawSingleISOUrl, "shutdown_command": &b.config.ShutdownCommand, - "ssh_key_path": &b.config.SSHKeyPath, - "ssh_password": &b.config.SSHPassword, - "ssh_username": &b.config.SSHUser, "virtualbox_version_file": &b.config.VBoxVersionFile, "vm_name": &b.config.VMName, "format": &b.config.Format, "boot_wait": &b.config.RawBootWait, "shutdown_timeout": &b.config.RawShutdownTimeout, - "ssh_wait_timeout": &b.config.RawSSHWaitTimeout, } for n, ptr := range templates { @@ -293,42 +271,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.RawShutdownTimeout = "5m" } - if b.config.RawSSHWaitTimeout == "" { - b.config.RawSSHWaitTimeout = "20m" - } - b.config.shutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout) if err != nil { errs = packer.MultiErrorAppend( errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) } - if b.config.SSHKeyPath != "" { - if _, err := os.Stat(b.config.SSHKeyPath); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("ssh_key_path is invalid: %s", err)) - } else if _, err := sshKeyToKeyring(b.config.SSHKeyPath); err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("ssh_key_path is invalid: %s", err)) - } - } - - if b.config.SSHHostPortMin > b.config.SSHHostPortMax { - errs = packer.MultiErrorAppend( - errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max")) - } - - if b.config.SSHUser == "" { - errs = packer.MultiErrorAppend( - errs, errors.New("An ssh_username must be specified.")) - } - - b.config.sshWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout) - if err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Failed parsing ssh_wait_timeout: %s", err)) - } - for i, args := range b.config.VBoxManage { for j, arg := range args { if err := b.config.tpl.Validate(arg); err != nil { @@ -382,14 +330,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepAttachISO), new(stepAttachGuestAdditions), new(vboxcommon.StepAttachFloppy), - new(stepForwardSSH), + &vboxcommon.StepForwardSSH{ + GuestPort: b.config.SSHPort, + HostPortMin: b.config.SSHHostPortMin, + HostPortMax: b.config.SSHHostPortMax, + }, new(stepVBoxManage), new(stepRun), new(stepTypeBootCommand), &common.StepConnectSSH{ - SSHAddress: sshAddress, - SSHConfig: sshConfig, - SSHWaitTimeout: b.config.sshWaitTimeout, + SSHAddress: vboxcommon.SSHAddress, + SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), + SSHWaitTimeout: b.config.SSHWaitTimeout, }, new(stepUploadVersion), new(stepUploadGuestAdditions), diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 0874c4b60..80e7754eb 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -8,36 +8,6 @@ import ( "testing" ) -var testPem = ` ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAxd4iamvrwRJvtNDGQSIbNvvIQN8imXTRWlRY62EvKov60vqu -hh+rDzFYAIIzlmrJopvOe0clqmi3mIP9dtkjPFrYflq52a2CF5q+BdwsJXuRHbJW -LmStZUwW1khSz93DhvhmK50nIaczW63u4EO/jJb3xj+wxR1Nkk9bxi3DDsYFt8SN -AzYx9kjlEYQ/+sI4/ATfmdV9h78SVotjScupd9KFzzi76gWq9gwyCBLRynTUWlyD -2UOfJRkOvhN6/jKzvYfVVwjPSfA9IMuooHdScmC4F6KBKJl/zf/zETM0XyzIDNmH -uOPbCiljq2WoRM+rY6ET84EO0kVXbfx8uxUsqQIDAQABAoIBAQCkPj9TF0IagbM3 -5BSs/CKbAWS4dH/D4bPlxx4IRCNirc8GUg+MRb04Xz0tLuajdQDqeWpr6iLZ0RKV -BvreLF+TOdV7DNQ4XE4gSdJyCtCaTHeort/aordL3l0WgfI7mVk0L/yfN1PEG4YG -E9q1TYcyrB3/8d5JwIkjabxERLglCcP+geOEJp+QijbvFIaZR/n2irlKW4gSy6ko -9B0fgUnhkHysSg49ChHQBPQ+o5BbpuLrPDFMiTPTPhdfsvGGcyCGeqfBA56oHcSF -K02Fg8OM+Bd1lb48LAN9nWWY4WbwV+9bkN3Ym8hO4c3a/Dxf2N7LtAQqWZzFjvM3 -/AaDvAgBAoGBAPLD+Xn1IYQPMB2XXCXfOuJewRY7RzoVWvMffJPDfm16O7wOiW5+ -2FmvxUDayk4PZy6wQMzGeGKnhcMMZTyaq2g/QtGfrvy7q1Lw2fB1VFlVblvqhoJa -nMJojjC4zgjBkXMHsRLeTmgUKyGs+fdFbfI6uejBnnf+eMVUMIdJ+6I9AoGBANCn -kWO9640dttyXURxNJ3lBr2H3dJOkmD6XS+u+LWqCSKQe691Y/fZ/ZL0Oc4Mhy7I6 -hsy3kDQ5k2V0fkaNODQIFJvUqXw2pMewUk8hHc9403f4fe9cPrL12rQ8WlQw4yoC -v2B61vNczCCUDtGxlAaw8jzSRaSI5s6ax3K7enbdAoGBAJB1WYDfA2CoAQO6y9Sl -b07A/7kQ8SN5DbPaqrDrBdJziBQxukoMJQXJeGFNUFD/DXFU5Fp2R7C86vXT7HIR -v6m66zH+CYzOx/YE6EsUJms6UP9VIVF0Rg/RU7teXQwM01ZV32LQ8mswhTH20o/3 -uqMHmxUMEhZpUMhrfq0isyApAoGAe1UxGTXfj9AqkIVYylPIq2HqGww7+jFmVEj1 -9Wi6S6Sq72ffnzzFEPkIQL/UA4TsdHMnzsYKFPSbbXLIWUeMGyVTmTDA5c0e5XIR -lPhMOKCAzv8w4VUzMnEkTzkFY5JqFCD/ojW57KvDdNZPVB+VEcdxyAW6aKELXMAc -eHLc1nkCgYEApm/motCTPN32nINZ+Vvywbv64ZD+gtpeMNP3CLrbe1X9O+H52AXa -1jCoOldWR8i2bs2NVPcKZgdo6fFULqE4dBX7Te/uYEIuuZhYLNzRO1IKU/YaqsXG -3bfQ8hKYcSnTfE0gPtLDnqCIxTocaGLSHeG3TH9fTw+dA8FvWpUztI4= ------END RSA PRIVATE KEY----- -` - func testConfig() map[string]interface{} { return map[string]interface{}{ "iso_checksum": "foo", @@ -77,22 +47,6 @@ func TestBuilderPrepare_Defaults(t *testing.T) { t.Errorf("bad guest OS type: %s", b.config.GuestOSType) } - if b.config.OutputDir != "output-foo" { - t.Errorf("bad output dir: %s", b.config.OutputDir) - } - - if b.config.SSHHostPortMin != 2222 { - t.Errorf("bad min ssh host port: %d", b.config.SSHHostPortMin) - } - - if b.config.SSHHostPortMax != 4444 { - t.Errorf("bad max ssh host port: %d", b.config.SSHHostPortMax) - } - - if b.config.SSHPort != 22 { - t.Errorf("bad ssh port: %d", b.config.SSHPort) - } - if b.config.VMName != "packer-foo" { t.Errorf("bad vm name: %s", b.config.VMName) } @@ -174,39 +128,6 @@ func TestBuilderPrepare_DiskSize(t *testing.T) { } } -func TestBuilderPrepare_FloppyFiles(t *testing.T) { - var b Builder - config := testConfig() - - delete(config, "floppy_files") - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("bad err: %s", err) - } - - if len(b.config.FloppyFiles) != 0 { - t.Fatalf("bad: %#v", b.config.FloppyFiles) - } - - config["floppy_files"] = []string{"foo", "bar"} - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - expected := []string{"foo", "bar"} - if !reflect.DeepEqual(b.config.FloppyFiles, expected) { - t.Fatalf("bad: %#v", b.config.FloppyFiles) - } -} - func TestBuilderPrepare_GuestAdditionsMode(t *testing.T) { var b Builder config := testConfig() @@ -613,39 +534,6 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { } } -func TestBuilderPrepare_OutputDir(t *testing.T) { - var b Builder - config := testConfig() - - // Test with existing dir - dir, err := ioutil.TempDir("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.RemoveAll(dir) - - config["output_directory"] = dir - b = Builder{} - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["output_directory"] = "i-hope-i-dont-exist" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - func TestBuilderPrepare_ShutdownCommand(t *testing.T) { var b Builder config := testConfig() @@ -687,173 +575,6 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { } } -func TestBuilderPrepare_SSHHostPort(t *testing.T) { - var b Builder - config := testConfig() - - // Bad - config["ssh_host_port_min"] = 1000 - config["ssh_host_port_max"] = 500 - b = Builder{} - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Bad - config["ssh_host_port_min"] = -500 - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Good - config["ssh_host_port_min"] = 500 - config["ssh_host_port_max"] = 1000 - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_sshKeyPath(t *testing.T) { - var b Builder - config := testConfig() - - config["ssh_key_path"] = "" - b = Builder{} - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - config["ssh_key_path"] = "/i/dont/exist" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test bad contents - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.Remove(tf.Name()) - defer tf.Close() - - if _, err := tf.Write([]byte("HELLO!")); err != nil { - t.Fatalf("err: %s", err) - } - - config["ssh_key_path"] = tf.Name() - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test good contents - tf.Seek(0, 0) - tf.Truncate(0) - tf.Write([]byte(testPem)) - config["ssh_key_path"] = tf.Name() - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestBuilderPrepare_SSHUser(t *testing.T) { - var b Builder - config := testConfig() - - config["ssh_username"] = "" - b = Builder{} - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - config["ssh_username"] = "exists" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { - var b Builder - config := testConfig() - - // Test a default boot_wait - delete(config, "ssh_wait_timeout") - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if b.config.RawSSHWaitTimeout != "20m" { - t.Fatalf("bad value: %s", b.config.RawSSHWaitTimeout) - } - - // Test with a bad value - config["ssh_wait_timeout"] = "this is not good" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["ssh_wait_timeout"] = "5s" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - func TestBuilderPrepare_VBoxManage(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/virtualbox/iso/ssh.go b/builder/virtualbox/iso/ssh.go deleted file mode 100644 index 2eb3cc8ab..000000000 --- a/builder/virtualbox/iso/ssh.go +++ /dev/null @@ -1,59 +0,0 @@ -package iso - -import ( - gossh "code.google.com/p/go.crypto/ssh" - "fmt" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/communicator/ssh" - "io/ioutil" - "os" -) - -func sshAddress(state multistep.StateBag) (string, error) { - sshHostPort := state.Get("sshHostPort").(uint) - return fmt.Sprintf("127.0.0.1:%d", sshHostPort), nil -} - -func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) { - config := state.Get("config").(*config) - - auth := []gossh.ClientAuth{ - gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)), - gossh.ClientAuthKeyboardInteractive( - ssh.PasswordKeyboardInteractive(config.SSHPassword)), - } - - if config.SSHKeyPath != "" { - keyring, err := sshKeyToKeyring(config.SSHKeyPath) - if err != nil { - return nil, err - } - - auth = append(auth, gossh.ClientAuthKeyring(keyring)) - } - - return &gossh.ClientConfig{ - User: config.SSHUser, - Auth: auth, - }, nil -} - -func sshKeyToKeyring(path string) (gossh.ClientKeyring, error) { - f, err := os.Open(path) - if err != nil { - return nil, err - } - defer f.Close() - - keyBytes, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - - keyring := new(ssh.SimpleKeychain) - if err := keyring.AddPEMKey(string(keyBytes)); err != nil { - return nil, err - } - - return keyring, nil -} diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 831fb3214..2484248f4 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -54,16 +54,21 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepAttachGuestAdditions), */ new(vboxcommon.StepAttachFloppy), + &vboxcommon.StepForwardSSH{ + GuestPort: b.config.SSHPort, + HostPortMin: b.config.SSHHostPortMin, + HostPortMax: b.config.SSHHostPortMax, + }, /* - new(stepForwardSSH), new(stepVBoxManage), new(stepRun), - new(stepTypeBootCommand), - &common.StepConnectSSH{ - SSHAddress: sshAddress, - SSHConfig: sshConfig, - SSHWaitTimeout: b.config.sshWaitTimeout, - }, + */ + &common.StepConnectSSH{ + SSHAddress: vboxcommon.SSHAddress, + SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), + SSHWaitTimeout: b.config.SSHWaitTimeout, + }, + /* new(stepUploadVersion), new(stepUploadGuestAdditions), new(common.StepProvision), diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 7dcf0da50..614c56cf9 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -11,6 +11,7 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate } @@ -30,7 +31,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Prepare the errors errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(c.tpl, &c.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(c.tpl)...) // Check for any errors. if errs != nil && len(errs.Errors) > 0 { From 4bd2aa6106777ee4a7e5ea7ad10f3a98d6d5ef8e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 09:24:29 -0800 Subject: [PATCH 12/30] builder/virtualbox: StepVBoxmanage --- .../{iso => common}/step_vboxmanage.go | 24 ++++--- .../virtualbox/common/vboxmanage_config.go | 28 ++++++++ .../common/vboxmanage_config_test.go | 37 +++++++++++ builder/virtualbox/iso/builder.go | 66 ++++++++----------- builder/virtualbox/iso/builder_test.go | 43 ------------ builder/virtualbox/ovf/builder.go | 8 ++- builder/virtualbox/ovf/config.go | 10 +-- 7 files changed, 119 insertions(+), 97 deletions(-) rename builder/virtualbox/{iso => common}/step_vboxmanage.go (69%) create mode 100644 builder/virtualbox/common/vboxmanage_config.go create mode 100644 builder/virtualbox/common/vboxmanage_config_test.go diff --git a/builder/virtualbox/iso/step_vboxmanage.go b/builder/virtualbox/common/step_vboxmanage.go similarity index 69% rename from builder/virtualbox/iso/step_vboxmanage.go rename to builder/virtualbox/common/step_vboxmanage.go index 62b8c0340..beb55da03 100644 --- a/builder/virtualbox/iso/step_vboxmanage.go +++ b/builder/virtualbox/common/step_vboxmanage.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "strings" ) @@ -16,17 +15,22 @@ type commandTemplate struct { // template. // // Uses: +// driver Driver +// ui packer.Ui +// vmName string // // Produces: -type stepVBoxManage struct{} +type StepVBoxManage struct { + Commands [][]string + Tpl *packer.ConfigTemplate +} -func (s *stepVBoxManage) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepVBoxManage) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) - if len(config.VBoxManage) > 0 { + if len(s.Commands) > 0 { ui.Say("Executing custom VBoxManage commands...") } @@ -34,13 +38,13 @@ func (s *stepVBoxManage) Run(state multistep.StateBag) multistep.StepAction { Name: vmName, } - for _, originalCommand := range config.VBoxManage { + for _, originalCommand := range s.Commands { command := make([]string, len(originalCommand)) copy(command, originalCommand) for i, arg := range command { var err error - command[i], err = config.tpl.Process(arg, tplData) + command[i], err = s.Tpl.Process(arg, tplData) if err != nil { err := fmt.Errorf("Error preparing vboxmanage command: %s", err) state.Put("error", err) @@ -61,4 +65,4 @@ func (s *stepVBoxManage) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepVBoxManage) Cleanup(state multistep.StateBag) {} +func (s *StepVBoxManage) Cleanup(state multistep.StateBag) {} diff --git a/builder/virtualbox/common/vboxmanage_config.go b/builder/virtualbox/common/vboxmanage_config.go new file mode 100644 index 000000000..b864a0422 --- /dev/null +++ b/builder/virtualbox/common/vboxmanage_config.go @@ -0,0 +1,28 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/packer/packer" +) + +type VBoxManageConfig struct { + VBoxManage [][]string `mapstructure:"vboxmanage"` +} + +func (c *VBoxManageConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.VBoxManage == nil { + c.VBoxManage = make([][]string, 0) + } + + errs := make([]error, 0) + for i, args := range c.VBoxManage { + for j, arg := range args { + if err := t.Validate(arg); err != nil { + errs = append(errs, + fmt.Errorf("Error processing vboxmanage[%d][%d]: %s", i, j, err)) + } + } + } + + return errs +} diff --git a/builder/virtualbox/common/vboxmanage_config_test.go b/builder/virtualbox/common/vboxmanage_config_test.go new file mode 100644 index 000000000..d089b977a --- /dev/null +++ b/builder/virtualbox/common/vboxmanage_config_test.go @@ -0,0 +1,37 @@ +package common + +import ( + "reflect" + "testing" +) + +func TestVBoxManageConfigPrepare_VBoxManage(t *testing.T) { + // Test with empty + c := new(VBoxManageConfig) + errs := c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + if !reflect.DeepEqual(c.VBoxManage, [][]string{}) { + t.Fatalf("bad: %#v", c.VBoxManage) + } + + // Test with a good one + c = new(VBoxManageConfig) + c.VBoxManage = [][]string{ + {"foo", "bar", "baz"}, + } + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + expected := [][]string{ + []string{"foo", "bar", "baz"}, + } + + if !reflect.DeepEqual(c.VBoxManage, expected) { + t.Fatalf("bad: %#v", c.VBoxManage) + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 0a7a21426..e5430bc1c 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -28,31 +28,31 @@ type Builder struct { } type config struct { - common.PackerConfig `mapstructure:",squash"` - vboxcommon.FloppyConfig `mapstructure:",squash"` - vboxcommon.OutputConfig `mapstructure:",squash"` - vboxcommon.SSHConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` + vboxcommon.VBoxManageConfig `mapstructure:",squash"` - BootCommand []string `mapstructure:"boot_command"` - DiskSize uint `mapstructure:"disk_size"` - Format string `mapstructure:"format"` - GuestAdditionsMode string `mapstructure:"guest_additions_mode"` - GuestAdditionsPath string `mapstructure:"guest_additions_path"` - GuestAdditionsURL string `mapstructure:"guest_additions_url"` - GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` - GuestOSType string `mapstructure:"guest_os_type"` - HardDriveInterface string `mapstructure:"hard_drive_interface"` - Headless bool `mapstructure:"headless"` - HTTPDir string `mapstructure:"http_directory"` - HTTPPortMin uint `mapstructure:"http_port_min"` - HTTPPortMax uint `mapstructure:"http_port_max"` - ISOChecksum string `mapstructure:"iso_checksum"` - ISOChecksumType string `mapstructure:"iso_checksum_type"` - ISOUrls []string `mapstructure:"iso_urls"` - ShutdownCommand string `mapstructure:"shutdown_command"` - VBoxVersionFile string `mapstructure:"virtualbox_version_file"` - VBoxManage [][]string `mapstructure:"vboxmanage"` - VMName string `mapstructure:"vm_name"` + BootCommand []string `mapstructure:"boot_command"` + DiskSize uint `mapstructure:"disk_size"` + Format string `mapstructure:"format"` + GuestAdditionsMode string `mapstructure:"guest_additions_mode"` + GuestAdditionsPath string `mapstructure:"guest_additions_path"` + GuestAdditionsURL string `mapstructure:"guest_additions_url"` + GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` + GuestOSType string `mapstructure:"guest_os_type"` + HardDriveInterface string `mapstructure:"hard_drive_interface"` + Headless bool `mapstructure:"headless"` + HTTPDir string `mapstructure:"http_directory"` + HTTPPortMin uint `mapstructure:"http_port_min"` + HTTPPortMax uint `mapstructure:"http_port_max"` + ISOChecksum string `mapstructure:"iso_checksum"` + ISOChecksumType string `mapstructure:"iso_checksum_type"` + ISOUrls []string `mapstructure:"iso_urls"` + ShutdownCommand string `mapstructure:"shutdown_command"` + VBoxVersionFile string `mapstructure:"virtualbox_version_file"` + VMName string `mapstructure:"vm_name"` RawBootWait string `mapstructure:"boot_wait"` RawSingleISOUrl string `mapstructure:"iso_url"` @@ -81,6 +81,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend( errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) + errs = packer.MultiErrorAppend(errs, b.config.VBoxManageConfig.Prepare(b.config.tpl)...) warnings := make([]string, 0) if b.config.DiskSize == 0 { @@ -115,10 +116,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.RawBootWait = "10s" } - if b.config.VBoxManage == nil { - b.config.VBoxManage = make([][]string, 0) - } - if b.config.VBoxVersionFile == "" { b.config.VBoxVersionFile = ".vbox_version" } @@ -277,15 +274,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) } - for i, args := range b.config.VBoxManage { - for j, arg := range args { - if err := b.config.tpl.Validate(arg); err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("Error processing vboxmanage[%d][%d]: %s", i, j, err)) - } - } - } - // Warnings if b.config.ShutdownCommand == "" { warnings = append(warnings, @@ -335,7 +323,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe HostPortMin: b.config.SSHHostPortMin, HostPortMax: b.config.SSHHostPortMax, }, - new(stepVBoxManage), + &vboxcommon.StepVBoxManage{ + Commands: b.config.VBoxManage, + }, new(stepRun), new(stepTypeBootCommand), &common.StepConnectSSH{ diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 80e7754eb..b8e2469a8 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -2,8 +2,6 @@ package iso import ( "github.com/mitchellh/packer/packer" - "io/ioutil" - "os" "reflect" "testing" ) @@ -575,47 +573,6 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { } } -func TestBuilderPrepare_VBoxManage(t *testing.T) { - var b Builder - config := testConfig() - - // Test with empty - delete(config, "vboxmanage") - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(b.config.VBoxManage, [][]string{}) { - t.Fatalf("bad: %#v", b.config.VBoxManage) - } - - // Test with a good one - config["vboxmanage"] = [][]interface{}{ - []interface{}{"foo", "bar", "baz"}, - } - - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - expected := [][]string{ - []string{"foo", "bar", "baz"}, - } - - if !reflect.DeepEqual(b.config.VBoxManage, expected) { - t.Fatalf("bad: %#v", b.config.VBoxManage) - } -} - func TestBuilderPrepare_VBoxVersionFile(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 2484248f4..1afcdc1c5 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -59,8 +59,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe HostPortMin: b.config.SSHHostPortMin, HostPortMax: b.config.SSHHostPortMax, }, + &vboxcommon.StepVBoxManage{ + Commands: b.config.VBoxManage, + }, /* - new(stepVBoxManage), new(stepRun), */ &common.StepConnectSSH{ @@ -71,7 +73,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe /* new(stepUploadVersion), new(stepUploadGuestAdditions), - new(common.StepProvision), + */ + new(common.StepProvision), + /* new(stepShutdown), new(stepRemoveDevices), new(stepExport), diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 614c56cf9..3ac1c80e0 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -8,10 +8,11 @@ import ( // Config is the configuration structure for the builder. type Config struct { - common.PackerConfig `mapstructure:",squash"` - vboxcommon.FloppyConfig `mapstructure:",squash"` - vboxcommon.OutputConfig `mapstructure:",squash"` - vboxcommon.SSHConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` + vboxcommon.VBoxManageConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate } @@ -34,6 +35,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(c.tpl, &c.PackerConfig)...) errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(c.tpl)...) + errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(c.tpl)...) // Check for any errors. if errs != nil && len(errs.Errors) > 0 { From 67a87ce3e4cc9f08ff2434349949f748a8e00100 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 09:37:27 -0800 Subject: [PATCH 13/30] builder/virtualbox: StepShutdown --- builder/virtualbox/common/shutdown_config.go | 42 +++++++++++++++++++ .../virtualbox/common/shutdown_config_test.go | 41 ++++++++++++++++++ .../{iso => common}/step_shutdown.go | 26 ++++++------ builder/virtualbox/iso/builder.go | 29 ++++--------- builder/virtualbox/iso/builder_test.go | 41 ------------------ builder/virtualbox/ovf/builder.go | 5 ++- builder/virtualbox/ovf/config.go | 17 ++++++-- 7 files changed, 122 insertions(+), 79 deletions(-) create mode 100644 builder/virtualbox/common/shutdown_config.go create mode 100644 builder/virtualbox/common/shutdown_config_test.go rename builder/virtualbox/{iso => common}/step_shutdown.go (69%) diff --git a/builder/virtualbox/common/shutdown_config.go b/builder/virtualbox/common/shutdown_config.go new file mode 100644 index 000000000..05e5fdfeb --- /dev/null +++ b/builder/virtualbox/common/shutdown_config.go @@ -0,0 +1,42 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/packer/packer" + "time" +) + +type ShutdownConfig struct { + ShutdownCommand string `mapstructure:"shutdown_command"` + RawShutdownTimeout string `mapstructure:"shutdown_timeout"` + + ShutdownTimeout time.Duration `` +} + +func (c *ShutdownConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.RawShutdownTimeout == "" { + c.RawShutdownTimeout = "5m" + } + + templates := map[string]*string{ + "shutdown_command": &c.ShutdownCommand, + "shutdown_timeout": &c.RawShutdownTimeout, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + var err error + c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout) + if err != nil { + errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) + } + + return errs +} diff --git a/builder/virtualbox/common/shutdown_config_test.go b/builder/virtualbox/common/shutdown_config_test.go new file mode 100644 index 000000000..b98b3a402 --- /dev/null +++ b/builder/virtualbox/common/shutdown_config_test.go @@ -0,0 +1,41 @@ +package common + +import ( + "testing" +) + +func testShutdownConfig() *ShutdownConfig { + return &ShutdownConfig{} +} + +func TestShutdownConfigPrepare_ShutdownCommand(t *testing.T) { + var c *ShutdownConfig + var errs []error + + c = testShutdownConfig() + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } +} + +func TestShutdownConfigPrepare_ShutdownTimeout(t *testing.T) { + var c *ShutdownConfig + var errs []error + + // Test with a bad value + c = testShutdownConfig() + c.RawShutdownTimeout = "this is not good" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("should have error") + } + + // Test with a good one + c = testShutdownConfig() + c.RawShutdownTimeout = "5s" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } +} diff --git a/builder/virtualbox/iso/step_shutdown.go b/builder/virtualbox/common/step_shutdown.go similarity index 69% rename from builder/virtualbox/iso/step_shutdown.go rename to builder/virtualbox/common/step_shutdown.go index e1f9d5028..3469dc7da 100644 --- a/builder/virtualbox/iso/step_shutdown.go +++ b/builder/virtualbox/common/step_shutdown.go @@ -1,10 +1,9 @@ -package iso +package common import ( "errors" "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "time" @@ -15,26 +14,27 @@ import ( // // Uses: // communicator packer.Communicator -// config *config // driver Driver // ui packer.Ui // vmName string // // Produces: // -type stepShutdown struct{} +type StepShutdown struct { + Command string + Timeout time.Duration +} -func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { +func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { comm := state.Get("communicator").(packer.Communicator) - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) - if config.ShutdownCommand != "" { + if s.Command != "" { ui.Say("Gracefully halting virtual machine...") - log.Printf("Executing shutdown command: %s", config.ShutdownCommand) - cmd := &packer.RemoteCmd{Command: config.ShutdownCommand} + log.Printf("Executing shutdown command: %s", s.Command) + cmd := &packer.RemoteCmd{Command: s.Command} if err := cmd.StartWithUi(comm, ui); err != nil { err := fmt.Errorf("Failed to send shutdown command: %s", err) state.Put("error", err) @@ -43,8 +43,8 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { } // Wait for the machine to actually shut down - log.Printf("Waiting max %s for shutdown to complete", config.shutdownTimeout) - shutdownTimer := time.After(config.shutdownTimeout) + log.Printf("Waiting max %s for shutdown to complete", s.Timeout) + shutdownTimer := time.After(s.Timeout) for { running, _ := driver.IsRunning(vmName) if !running { @@ -75,4 +75,4 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepShutdown) Cleanup(state multistep.StateBag) {} +func (s *StepShutdown) Cleanup(state multistep.StateBag) {} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index e5430bc1c..25c4aee05 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -31,6 +31,7 @@ type config struct { common.PackerConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.SSHConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` @@ -50,17 +51,14 @@ type config struct { ISOChecksum string `mapstructure:"iso_checksum"` ISOChecksumType string `mapstructure:"iso_checksum_type"` ISOUrls []string `mapstructure:"iso_urls"` - ShutdownCommand string `mapstructure:"shutdown_command"` VBoxVersionFile string `mapstructure:"virtualbox_version_file"` VMName string `mapstructure:"vm_name"` - RawBootWait string `mapstructure:"boot_wait"` - RawSingleISOUrl string `mapstructure:"iso_url"` - RawShutdownTimeout string `mapstructure:"shutdown_timeout"` + RawBootWait string `mapstructure:"boot_wait"` + RawSingleISOUrl string `mapstructure:"iso_url"` - bootWait time.Duration `` - shutdownTimeout time.Duration `` - tpl *packer.ConfigTemplate + bootWait time.Duration `` + tpl *packer.ConfigTemplate } func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { @@ -138,12 +136,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { "iso_checksum": &b.config.ISOChecksum, "iso_checksum_type": &b.config.ISOChecksumType, "iso_url": &b.config.RawSingleISOUrl, - "shutdown_command": &b.config.ShutdownCommand, "virtualbox_version_file": &b.config.VBoxVersionFile, "vm_name": &b.config.VMName, "format": &b.config.Format, "boot_wait": &b.config.RawBootWait, - "shutdown_timeout": &b.config.RawShutdownTimeout, } for n, ptr := range templates { @@ -264,16 +260,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) } - if b.config.RawShutdownTimeout == "" { - b.config.RawShutdownTimeout = "5m" - } - - b.config.shutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout) - if err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) - } - // Warnings if b.config.ShutdownCommand == "" { warnings = append(warnings, @@ -336,7 +322,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepUploadVersion), new(stepUploadGuestAdditions), new(common.StepProvision), - new(stepShutdown), + &vboxcommon.StepShutdown{ + Command: b.config.ShutdownCommand, + Timeout: b.config.ShutdownTimeout, + }, new(stepRemoveDevices), new(stepExport), } diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index b8e2469a8..7e7bf4ead 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -532,47 +532,6 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { } } -func TestBuilderPrepare_ShutdownCommand(t *testing.T) { - var b Builder - config := testConfig() - delete(config, "shutdown_command") - - warns, err := b.Prepare(config) - if err != nil { - t.Fatalf("bad: %s", err) - } - - if len(warns) != 1 { - t.Fatalf("bad: %#v", warns) - } -} - -func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { - var b Builder - config := testConfig() - - // Test with a bad value - config["shutdown_timeout"] = "this is not good" - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["shutdown_timeout"] = "5s" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - func TestBuilderPrepare_VBoxVersionFile(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 1afcdc1c5..765da1c94 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -75,8 +75,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepUploadGuestAdditions), */ new(common.StepProvision), + &vboxcommon.StepShutdown{ + Command: b.config.ShutdownCommand, + Timeout: b.config.ShutdownTimeout, + }, /* - new(stepShutdown), new(stepRemoveDevices), new(stepExport), */ diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 3ac1c80e0..999173ac0 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -12,6 +12,7 @@ type Config struct { vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` vboxcommon.SSHConfig `mapstructure:",squash"` + vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate @@ -37,10 +38,18 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(c.tpl)...) - // Check for any errors. - if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + // Warnings + var warnings []string + if c.ShutdownCommand == "" { + warnings = append(warnings, + "A shutdown_command was not specified. Without a shutdown command, Packer\n"+ + "will forcibly halt the virtual machine, which may result in data loss.") } - return c, nil, nil + // Check for any errors. + if errs != nil && len(errs.Errors) > 0 { + return nil, warnings, errs + } + + return c, warnings, nil } From b65559d888e0ff0ab9d25575914ce57bfee2ee7d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 09:47:44 -0800 Subject: [PATCH 14/30] builder/virtualbox: StepShutdown tests --- builder/virtualbox/common/driver_mock.go | 7 ++ builder/virtualbox/common/step_shutdown.go | 2 +- .../virtualbox/common/step_shutdown_test.go | 105 ++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 builder/virtualbox/common/step_shutdown_test.go diff --git a/builder/virtualbox/common/driver_mock.go b/builder/virtualbox/common/driver_mock.go index ad00ae248..91b587070 100644 --- a/builder/virtualbox/common/driver_mock.go +++ b/builder/virtualbox/common/driver_mock.go @@ -1,6 +1,10 @@ package common +import "sync" + type DriverMock struct { + sync.Mutex + CreateSATAControllerVM string CreateSATAControllerController string CreateSATAControllerErr error @@ -33,6 +37,9 @@ func (d *DriverMock) CreateSATAController(vm string, controller string) error { } func (d *DriverMock) IsRunning(name string) (bool, error) { + d.Lock() + defer d.Unlock() + d.IsRunningName = name return d.IsRunningReturn, d.IsRunningErr } diff --git a/builder/virtualbox/common/step_shutdown.go b/builder/virtualbox/common/step_shutdown.go index 3469dc7da..0f33692ce 100644 --- a/builder/virtualbox/common/step_shutdown.go +++ b/builder/virtualbox/common/step_shutdown.go @@ -58,7 +58,7 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { ui.Error(err.Error()) return multistep.ActionHalt default: - time.Sleep(1 * time.Second) + time.Sleep(500 * time.Millisecond) } } } else { diff --git a/builder/virtualbox/common/step_shutdown_test.go b/builder/virtualbox/common/step_shutdown_test.go new file mode 100644 index 000000000..215eefd30 --- /dev/null +++ b/builder/virtualbox/common/step_shutdown_test.go @@ -0,0 +1,105 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "testing" + "time" +) + +func TestStepShutdown_impl(t *testing.T) { + var _ multistep.Step = new(StepShutdown) +} + +func TestStepShutdown_noShutdownCommand(t *testing.T) { + state := testState(t) + step := new(StepShutdown) + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test that Stop was just called + if driver.StopName != "foo" { + t.Fatal("should call stop") + } + if comm.StartCalled { + t.Fatal("comm start should not be called") + } +} + +func TestStepShutdown_shutdownCommand(t *testing.T) { + state := testState(t) + step := new(StepShutdown) + step.Command = "poweroff" + step.Timeout = 1 * time.Second + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + driver.IsRunningReturn = true + + go func() { + time.Sleep(10 * time.Millisecond) + driver.Lock() + defer driver.Unlock() + driver.IsRunningReturn = false + }() + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test that Stop was just called + if driver.StopName != "" { + t.Fatal("should not call stop") + } + if comm.StartCmd.Command != step.Command { + t.Fatal("comm start should be called") + } +} + +func TestStepShutdown_shutdownTimeout(t *testing.T) { + state := testState(t) + step := new(StepShutdown) + step.Command = "poweroff" + step.Timeout = 1 * time.Second + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + driver.IsRunningReturn = true + + go func() { + time.Sleep(2 * time.Second) + driver.Lock() + defer driver.Unlock() + driver.IsRunningReturn = false + }() + + // Test the run + if action := step.Run(state); action != multistep.ActionHalt { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); !ok { + t.Fatal("should have error") + } +} From fbd20dffcbd1e34db5901806e7bfce1d6517e2d0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 09:54:00 -0800 Subject: [PATCH 15/30] builder/virtualbox/common: StepRemoveDevices --- .../{iso => common}/step_remove_devices.go | 14 ++-- .../common/step_remove_devices_test.go | 64 +++++++++++++++++++ builder/virtualbox/iso/builder.go | 2 +- builder/virtualbox/ovf/builder.go | 2 +- 4 files changed, 74 insertions(+), 8 deletions(-) rename builder/virtualbox/{iso => common}/step_remove_devices.go (80%) create mode 100644 builder/virtualbox/common/step_remove_devices_test.go diff --git a/builder/virtualbox/iso/step_remove_devices.go b/builder/virtualbox/common/step_remove_devices.go similarity index 80% rename from builder/virtualbox/iso/step_remove_devices.go rename to builder/virtualbox/common/step_remove_devices.go index 763e22670..176de50a4 100644 --- a/builder/virtualbox/iso/step_remove_devices.go +++ b/builder/virtualbox/common/step_remove_devices.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" ) @@ -11,12 +10,15 @@ import ( // machine that we may have added. // // Uses: +// driver Driver +// ui packer.Ui +// vmName string // // Produces: -type stepRemoveDevices struct{} +type StepRemoveDevices struct{} -func (s *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -56,5 +58,5 @@ func (s *stepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepRemoveDevices) Cleanup(state multistep.StateBag) { +func (s *StepRemoveDevices) Cleanup(state multistep.StateBag) { } diff --git a/builder/virtualbox/common/step_remove_devices_test.go b/builder/virtualbox/common/step_remove_devices_test.go new file mode 100644 index 000000000..af1bccc1f --- /dev/null +++ b/builder/virtualbox/common/step_remove_devices_test.go @@ -0,0 +1,64 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "testing" +) + +func TestStepRemoveDevices_impl(t *testing.T) { + var _ multistep.Step = new(StepRemoveDevices) +} + +func TestStepRemoveDevices(t *testing.T) { + state := testState(t) + step := new(StepRemoveDevices) + + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test that ISO was removed + if len(driver.VBoxManageCalls) != 1 { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } + if driver.VBoxManageCalls[0][3] != "IDE Controller" { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } +} + +func TestStepRemoveDevices_floppyPath(t *testing.T) { + state := testState(t) + step := new(StepRemoveDevices) + + state.Put("floppy_path", "foo") + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test that both were removed + if len(driver.VBoxManageCalls) != 2 { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } + if driver.VBoxManageCalls[0][3] != "Floppy Controller" { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } + if driver.VBoxManageCalls[1][3] != "IDE Controller" { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 25c4aee05..ab5ae993d 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -326,7 +326,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Command: b.config.ShutdownCommand, Timeout: b.config.ShutdownTimeout, }, - new(stepRemoveDevices), + new(vboxcommon.StepRemoveDevices), new(stepExport), } diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 765da1c94..4cd7d2e52 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -79,8 +79,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Command: b.config.ShutdownCommand, Timeout: b.config.ShutdownTimeout, }, + new(vboxcommon.StepRemoveDevices), /* - new(stepRemoveDevices), new(stepExport), */ } From 026bcd33fe363e2a85d89f54eaeb9de045922b9e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 10:30:12 -0800 Subject: [PATCH 16/30] builder/virtualbox: StepRun common --- builder/virtualbox/common/run_config.go | 41 +++++++++++++++++++ builder/virtualbox/common/run_config_test.go | 37 +++++++++++++++++ .../virtualbox/{iso => common}/step_run.go | 28 +++++++------ builder/virtualbox/iso/builder.go | 24 ++++------- builder/virtualbox/iso/builder_test.go | 40 ------------------ builder/virtualbox/ovf/builder.go | 7 ++-- builder/virtualbox/ovf/config.go | 2 + 7 files changed, 107 insertions(+), 72 deletions(-) create mode 100644 builder/virtualbox/common/run_config.go create mode 100644 builder/virtualbox/common/run_config_test.go rename builder/virtualbox/{iso => common}/step_run.go (72%) diff --git a/builder/virtualbox/common/run_config.go b/builder/virtualbox/common/run_config.go new file mode 100644 index 000000000..755d0f1c1 --- /dev/null +++ b/builder/virtualbox/common/run_config.go @@ -0,0 +1,41 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/packer/packer" + "time" +) + +type RunConfig struct { + Headless bool `mapstructure:"headless"` + RawBootWait string `mapstructure:"boot_wait"` + + BootWait time.Duration `` +} + +func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.RawBootWait == "" { + c.RawBootWait = "10s" + } + + templates := map[string]*string{ + "boot_wait": &c.RawBootWait, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + var err error + c.BootWait, err = time.ParseDuration(c.RawBootWait) + if err != nil { + errs = append(errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) + } + + return errs +} diff --git a/builder/virtualbox/common/run_config_test.go b/builder/virtualbox/common/run_config_test.go new file mode 100644 index 000000000..8068fe625 --- /dev/null +++ b/builder/virtualbox/common/run_config_test.go @@ -0,0 +1,37 @@ +package common + +import ( + "testing" +) + +func TestRunConfigPrepare_BootWait(t *testing.T) { + var c *RunConfig + var errs []error + + // Test a default boot_wait + c = new(RunConfig) + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + if c.RawBootWait != "10s" { + t.Fatalf("bad value: %s", c.RawBootWait) + } + + // Test with a bad boot_wait + c = new(RunConfig) + c.RawBootWait = "this is not good" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("bad: %#v", errs) + } + + // Test with a good one + c = new(RunConfig) + c.RawBootWait = "5s" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } +} diff --git a/builder/virtualbox/iso/step_run.go b/builder/virtualbox/common/step_run.go similarity index 72% rename from builder/virtualbox/iso/step_run.go rename to builder/virtualbox/common/step_run.go index 1d5cc63be..0718ad7f3 100644 --- a/builder/virtualbox/iso/step_run.go +++ b/builder/virtualbox/common/step_run.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "time" ) @@ -11,21 +10,26 @@ import ( // This step starts the virtual machine. // // Uses: +// driver Driver +// ui packer.Ui +// vmName string // // Produces: -type stepRun struct { +type StepRun struct { + BootWait time.Duration + Headless bool + vmName string } -func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepRun) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) ui.Say("Starting the virtual machine...") guiArgument := "gui" - if config.Headless == true { + if s.Headless == true { ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" + "In headless mode, errors during the boot sequence or OS setup\n" + "won't be easily visible. Use at your own discretion.") @@ -41,9 +45,9 @@ func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction { s.vmName = vmName - if int64(config.bootWait) > 0 { - ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait)) - wait := time.After(config.bootWait) + if int64(s.BootWait) > 0 { + ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait)) + wait := time.After(s.BootWait) WAITLOOP: for { select { @@ -60,12 +64,12 @@ func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepRun) Cleanup(state multistep.StateBag) { +func (s *StepRun) Cleanup(state multistep.StateBag) { if s.vmName == "" { return } - driver := state.Get("driver").(vboxcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) if running, _ := driver.IsRunning(s.vmName); running { diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index ab5ae993d..86a351ae6 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -9,7 +9,6 @@ import ( "github.com/mitchellh/packer/packer" "log" "strings" - "time" ) const BuilderId = "mitchellh.virtualbox" @@ -31,6 +30,7 @@ type config struct { common.PackerConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.RunConfig `mapstructure:",squash"` vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.SSHConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` @@ -44,7 +44,6 @@ type config struct { GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` GuestOSType string `mapstructure:"guest_os_type"` HardDriveInterface string `mapstructure:"hard_drive_interface"` - Headless bool `mapstructure:"headless"` HTTPDir string `mapstructure:"http_directory"` HTTPPortMin uint `mapstructure:"http_port_min"` HTTPPortMax uint `mapstructure:"http_port_max"` @@ -54,11 +53,9 @@ type config struct { VBoxVersionFile string `mapstructure:"virtualbox_version_file"` VMName string `mapstructure:"vm_name"` - RawBootWait string `mapstructure:"boot_wait"` RawSingleISOUrl string `mapstructure:"iso_url"` - bootWait time.Duration `` - tpl *packer.ConfigTemplate + tpl *packer.ConfigTemplate } func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { @@ -78,6 +75,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend( errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.VBoxManageConfig.Prepare(b.config.tpl)...) warnings := make([]string, 0) @@ -110,10 +108,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.HTTPPortMax = 9000 } - if b.config.RawBootWait == "" { - b.config.RawBootWait = "10s" - } - if b.config.VBoxVersionFile == "" { b.config.VBoxVersionFile = ".vbox_version" } @@ -139,7 +133,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { "virtualbox_version_file": &b.config.VBoxVersionFile, "vm_name": &b.config.VMName, "format": &b.config.Format, - "boot_wait": &b.config.RawBootWait, } for n, ptr := range templates { @@ -254,12 +247,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.GuestAdditionsSHA256 = strings.ToLower(b.config.GuestAdditionsSHA256) } - b.config.bootWait, err = time.ParseDuration(b.config.RawBootWait) - if err != nil { - errs = packer.MultiErrorAppend( - errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) - } - // Warnings if b.config.ShutdownCommand == "" { warnings = append(warnings, @@ -312,7 +299,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vboxcommon.StepVBoxManage{ Commands: b.config.VBoxManage, }, - new(stepRun), + &vboxcommon.StepRun{ + BootWait: b.config.BootWait, + Headless: b.config.Headless, + }, new(stepTypeBootCommand), &common.StepConnectSSH{ SSHAddress: vboxcommon.SSHAddress, diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 7e7bf4ead..fa60e482b 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -54,46 +54,6 @@ func TestBuilderPrepare_Defaults(t *testing.T) { } } -func TestBuilderPrepare_BootWait(t *testing.T) { - var b Builder - config := testConfig() - - // Test a default boot_wait - delete(config, "boot_wait") - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if b.config.RawBootWait != "10s" { - t.Fatalf("bad value: %s", b.config.RawBootWait) - } - - // Test with a bad boot_wait - config["boot_wait"] = "this is not good" - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["boot_wait"] = "5s" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - func TestBuilderPrepare_DiskSize(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 4cd7d2e52..2317a4601 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -62,9 +62,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vboxcommon.StepVBoxManage{ Commands: b.config.VBoxManage, }, - /* - new(stepRun), - */ + &vboxcommon.StepRun{ + BootWait: b.config.BootWait, + Headless: b.config.Headless, + }, &common.StepConnectSSH{ SSHAddress: vboxcommon.SSHAddress, SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 999173ac0..929bb24a5 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -11,6 +11,7 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.RunConfig `mapstructure:",squash"` vboxcommon.SSHConfig `mapstructure:",squash"` vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` @@ -35,6 +36,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs := common.CheckUnusedConfig(md) errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(c.tpl, &c.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(c.tpl)...) From 5feb7bce188aa05048a9a7cbfae206dcf414ace5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 10:40:39 -0800 Subject: [PATCH 17/30] builder/virtualbox/common: Export --- builder/virtualbox/common/export_config.go | 37 ++++++++++++++++ .../virtualbox/common/export_config_test.go | 34 +++++++++++++++ .../virtualbox/{iso => common}/step_export.go | 17 ++++---- builder/virtualbox/common/step_export_test.go | 43 +++++++++++++++++++ builder/virtualbox/iso/builder.go | 18 +++----- builder/virtualbox/iso/builder_test.go | 37 ---------------- builder/virtualbox/ovf/builder.go | 7 +-- builder/virtualbox/ovf/config.go | 2 + 8 files changed, 135 insertions(+), 60 deletions(-) create mode 100644 builder/virtualbox/common/export_config.go create mode 100644 builder/virtualbox/common/export_config_test.go rename builder/virtualbox/{iso => common}/step_export.go (78%) create mode 100644 builder/virtualbox/common/step_export_test.go diff --git a/builder/virtualbox/common/export_config.go b/builder/virtualbox/common/export_config.go new file mode 100644 index 000000000..364d86a84 --- /dev/null +++ b/builder/virtualbox/common/export_config.go @@ -0,0 +1,37 @@ +package common + +import ( + "errors" + "fmt" + "github.com/mitchellh/packer/packer" +) + +type ExportConfig struct { + Format string `mapstruture:"format"` +} + +func (c *ExportConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.Format == "" { + c.Format = "ovf" + } + + templates := map[string]*string{ + "format": &c.Format, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + if c.Format != "ovf" && c.Format != "ova" { + errs = append(errs, + errors.New("invalid format, only 'ovf' or 'ova' are allowed")) + } + + return errs +} diff --git a/builder/virtualbox/common/export_config_test.go b/builder/virtualbox/common/export_config_test.go new file mode 100644 index 000000000..612bfc01d --- /dev/null +++ b/builder/virtualbox/common/export_config_test.go @@ -0,0 +1,34 @@ +package common + +import ( + "testing" +) + +func TestExportConfigPrepare_BootWait(t *testing.T) { + var c *ExportConfig + var errs []error + + // Bad + c = new(ExportConfig) + c.Format = "illega" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("bad: %#v", errs) + } + + // Good + c = new(ExportConfig) + c.Format = "ova" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + // Good + c = new(ExportConfig) + c.Format = "ovf" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } +} diff --git a/builder/virtualbox/iso/step_export.go b/builder/virtualbox/common/step_export.go similarity index 78% rename from builder/virtualbox/iso/step_export.go rename to builder/virtualbox/common/step_export.go index 5e5f5e8f6..c5dc208b2 100644 --- a/builder/virtualbox/iso/step_export.go +++ b/builder/virtualbox/common/step_export.go @@ -1,9 +1,8 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" "path/filepath" @@ -16,11 +15,13 @@ import ( // // Produces: // exportPath string - The path to the resulting export. -type stepExport struct{} +type StepExport struct { + Format string + OutputDir string +} -func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepExport) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -42,7 +43,7 @@ func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { } // Export the VM to an OVF - outputPath := filepath.Join(config.OutputDir, vmName+"."+config.Format) + outputPath := filepath.Join(s.OutputDir, vmName+"."+s.Format) command = []string{ "export", @@ -65,4 +66,4 @@ func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepExport) Cleanup(state multistep.StateBag) {} +func (s *StepExport) Cleanup(state multistep.StateBag) {} diff --git a/builder/virtualbox/common/step_export_test.go b/builder/virtualbox/common/step_export_test.go new file mode 100644 index 000000000..0a939d331 --- /dev/null +++ b/builder/virtualbox/common/step_export_test.go @@ -0,0 +1,43 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "testing" +) + +func TestStepExport_impl(t *testing.T) { + var _ multistep.Step = new(StepExport) +} + +func TestStepExport(t *testing.T) { + state := testState(t) + step := new(StepExport) + + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test output state + if _, ok := state.GetOk("exportPath"); !ok { + t.Fatal("should set exportPath") + } + + // Test driver + if len(driver.VBoxManageCalls) != 2 { + t.Fatal("should call vboxmanage") + } + if driver.VBoxManageCalls[0][0] != "modifyvm" { + t.Fatal("bad") + } + if driver.VBoxManageCalls[1][0] != "export" { + t.Fatal("bad") + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 86a351ae6..8ea630695 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -28,6 +28,7 @@ type Builder struct { type config struct { common.PackerConfig `mapstructure:",squash"` + vboxcommon.ExportConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` vboxcommon.RunConfig `mapstructure:",squash"` @@ -37,7 +38,6 @@ type config struct { BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` - Format string `mapstructure:"format"` GuestAdditionsMode string `mapstructure:"guest_additions_mode"` GuestAdditionsPath string `mapstructure:"guest_additions_path"` GuestAdditionsURL string `mapstructure:"guest_additions_url"` @@ -72,6 +72,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Accumulate any errors and warnings errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend( errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) @@ -116,10 +117,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) } - if b.config.Format == "" { - b.config.Format = "ovf" - } - // Errors templates := map[string]*string{ "guest_additions_mode": &b.config.GuestAdditionsMode, @@ -132,7 +129,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { "iso_url": &b.config.RawSingleISOUrl, "virtualbox_version_file": &b.config.VBoxVersionFile, "vm_name": &b.config.VMName, - "format": &b.config.Format, } for n, ptr := range templates { @@ -172,11 +168,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } - if !(b.config.Format == "ovf" || b.config.Format == "ova") { - errs = packer.MultiErrorAppend( - errs, errors.New("invalid format, only 'ovf' or 'ova' are allowed")) - } - if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" { errs = packer.MultiErrorAppend( errs, errors.New("hard_drive_interface can only be ide or sata")) @@ -317,7 +308,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Timeout: b.config.ShutdownTimeout, }, new(vboxcommon.StepRemoveDevices), - new(stepExport), + &vboxcommon.StepExport{ + Format: b.config.Format, + OutputDir: b.config.OutputDir, + }, } // Setup the state bag diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index fa60e482b..50d075999 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -299,43 +299,6 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) { } } -func TestBuilderPrepare_Format(t *testing.T) { - var b Builder - config := testConfig() - - // Bad - config["format"] = "illegal value" - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Good - config["format"] = "ova" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - // Good - config["format"] = "ovf" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - func TestBuilderPrepare_InvalidKey(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 2317a4601..a8cb3b903 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -81,9 +81,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Timeout: b.config.ShutdownTimeout, }, new(vboxcommon.StepRemoveDevices), - /* - new(stepExport), - */ + &vboxcommon.StepExport{ + Format: b.config.Format, + OutputDir: b.config.OutputDir, + }, } // Run the steps. diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 929bb24a5..2c2442bc8 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -9,6 +9,7 @@ import ( // Config is the configuration structure for the builder. type Config struct { common.PackerConfig `mapstructure:",squash"` + vboxcommon.ExportConfig `mapstructure:",squash"` vboxcommon.FloppyConfig `mapstructure:",squash"` vboxcommon.OutputConfig `mapstructure:",squash"` vboxcommon.RunConfig `mapstructure:",squash"` @@ -34,6 +35,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Prepare the errors errs := common.CheckUnusedConfig(md) + errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.OutputConfig.Prepare(c.tpl, &c.PackerConfig)...) errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(c.tpl)...) From 5f1c5972694c9a7df90a357474e8cd608c9fd89e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 11:50:29 -0800 Subject: [PATCH 18/30] builder/virtualbox: StepUploadVersion --- .../{iso => common}/step_upload_version.go | 18 +++++----- .../virtualbox/common/vbox_version_config.go | 31 +++++++++++++++++ .../common/vbox_version_config_test.go | 33 ++++++++++++++++++ builder/virtualbox/iso/builder.go | 30 ++++++++-------- builder/virtualbox/iso/builder_test.go | 34 ------------------- builder/virtualbox/ovf/builder.go | 4 ++- builder/virtualbox/ovf/config.go | 2 ++ 7 files changed, 92 insertions(+), 60 deletions(-) rename builder/virtualbox/{iso => common}/step_upload_version.go (66%) create mode 100644 builder/virtualbox/common/vbox_version_config.go create mode 100644 builder/virtualbox/common/vbox_version_config_test.go diff --git a/builder/virtualbox/iso/step_upload_version.go b/builder/virtualbox/common/step_upload_version.go similarity index 66% rename from builder/virtualbox/iso/step_upload_version.go rename to builder/virtualbox/common/step_upload_version.go index 3517d4f82..1bfd526a6 100644 --- a/builder/virtualbox/iso/step_upload_version.go +++ b/builder/virtualbox/common/step_upload_version.go @@ -1,25 +1,25 @@ -package iso +package common import ( "bytes" "fmt" "github.com/mitchellh/multistep" - vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/packer" "log" ) // This step uploads a file containing the VirtualBox version, which // can be useful for various provisioning reasons. -type stepUploadVersion struct{} +type StepUploadVersion struct { + Path string +} -func (s *stepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { +func (s *StepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { comm := state.Get("communicator").(packer.Communicator) - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) - if config.VBoxVersionFile == "" { + if s.Path == "" { log.Println("VBoxVersionFile is empty. Not uploading.") return multistep.ActionContinue } @@ -33,7 +33,7 @@ func (s *stepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { ui.Say(fmt.Sprintf("Uploading VirtualBox version info (%s)", version)) var data bytes.Buffer data.WriteString(version) - if err := comm.Upload(config.VBoxVersionFile, &data); err != nil { + if err := comm.Upload(s.Path, &data); err != nil { state.Put("error", fmt.Errorf("Error uploading VirtualBox version: %s", err)) return multistep.ActionHalt } @@ -41,4 +41,4 @@ func (s *stepUploadVersion) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepUploadVersion) Cleanup(state multistep.StateBag) {} +func (s *StepUploadVersion) Cleanup(state multistep.StateBag) {} diff --git a/builder/virtualbox/common/vbox_version_config.go b/builder/virtualbox/common/vbox_version_config.go new file mode 100644 index 000000000..ff2c14819 --- /dev/null +++ b/builder/virtualbox/common/vbox_version_config.go @@ -0,0 +1,31 @@ +package common + +import ( + "fmt" + "github.com/mitchellh/packer/packer" +) + +type VBoxVersionConfig struct { + VBoxVersionFile string `mapstructure:"virtualbox_version_file"` +} + +func (c *VBoxVersionConfig) Prepare(t *packer.ConfigTemplate) []error { + if c.VBoxVersionFile == "" { + c.VBoxVersionFile = ".vbox_version" + } + + templates := map[string]*string{ + "virtualbox_version_file": &c.VBoxVersionFile, + } + + errs := make([]error, 0) + for n, ptr := range templates { + var err error + *ptr, err = t.Process(*ptr, nil) + if err != nil { + errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + + return errs +} diff --git a/builder/virtualbox/common/vbox_version_config_test.go b/builder/virtualbox/common/vbox_version_config_test.go new file mode 100644 index 000000000..d4cc85565 --- /dev/null +++ b/builder/virtualbox/common/vbox_version_config_test.go @@ -0,0 +1,33 @@ +package common + +import ( + "testing" +) + +func TestVBoxVersionConfigPrepare_BootWait(t *testing.T) { + var c *VBoxVersionConfig + var errs []error + + // Test empty + c = new(VBoxVersionConfig) + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + if c.VBoxVersionFile != ".vbox_version" { + t.Fatalf("bad value: %s", c.VBoxVersionFile) + } + + // Test with a good one + c = new(VBoxVersionConfig) + c.VBoxVersionFile = "foo" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } + + if c.VBoxVersionFile != "foo" { + t.Fatalf("bad value: %s", c.VBoxVersionFile) + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 8ea630695..d5402087c 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -35,6 +35,7 @@ type config struct { vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.SSHConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` + vboxcommon.VBoxVersionConfig `mapstructure:",squash"` BootCommand []string `mapstructure:"boot_command"` DiskSize uint `mapstructure:"disk_size"` @@ -50,7 +51,6 @@ type config struct { ISOChecksum string `mapstructure:"iso_checksum"` ISOChecksumType string `mapstructure:"iso_checksum_type"` ISOUrls []string `mapstructure:"iso_urls"` - VBoxVersionFile string `mapstructure:"virtualbox_version_file"` VMName string `mapstructure:"vm_name"` RawSingleISOUrl string `mapstructure:"iso_url"` @@ -79,6 +79,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) errs = packer.MultiErrorAppend(errs, b.config.VBoxManageConfig.Prepare(b.config.tpl)...) + errs = packer.MultiErrorAppend(errs, b.config.VBoxVersionConfig.Prepare(b.config.tpl)...) warnings := make([]string, 0) if b.config.DiskSize == 0 { @@ -109,26 +110,21 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.HTTPPortMax = 9000 } - if b.config.VBoxVersionFile == "" { - b.config.VBoxVersionFile = ".vbox_version" - } - if b.config.VMName == "" { b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName) } // Errors templates := map[string]*string{ - "guest_additions_mode": &b.config.GuestAdditionsMode, - "guest_additions_sha256": &b.config.GuestAdditionsSHA256, - "guest_os_type": &b.config.GuestOSType, - "hard_drive_interface": &b.config.HardDriveInterface, - "http_directory": &b.config.HTTPDir, - "iso_checksum": &b.config.ISOChecksum, - "iso_checksum_type": &b.config.ISOChecksumType, - "iso_url": &b.config.RawSingleISOUrl, - "virtualbox_version_file": &b.config.VBoxVersionFile, - "vm_name": &b.config.VMName, + "guest_additions_mode": &b.config.GuestAdditionsMode, + "guest_additions_sha256": &b.config.GuestAdditionsSHA256, + "guest_os_type": &b.config.GuestOSType, + "hard_drive_interface": &b.config.HardDriveInterface, + "http_directory": &b.config.HTTPDir, + "iso_checksum": &b.config.ISOChecksum, + "iso_checksum_type": &b.config.ISOChecksumType, + "iso_url": &b.config.RawSingleISOUrl, + "vm_name": &b.config.VMName, } for n, ptr := range templates { @@ -300,7 +296,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), SSHWaitTimeout: b.config.SSHWaitTimeout, }, - new(stepUploadVersion), + &vboxcommon.StepUploadVersion{ + Path: b.config.VBoxVersionFile, + }, new(stepUploadGuestAdditions), new(common.StepProvision), &vboxcommon.StepShutdown{ diff --git a/builder/virtualbox/iso/builder_test.go b/builder/virtualbox/iso/builder_test.go index 50d075999..85d3f4d0e 100644 --- a/builder/virtualbox/iso/builder_test.go +++ b/builder/virtualbox/iso/builder_test.go @@ -454,37 +454,3 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) { t.Fatalf("bad: %#v", b.config.ISOUrls) } } - -func TestBuilderPrepare_VBoxVersionFile(t *testing.T) { - var b Builder - config := testConfig() - - // Test empty - delete(config, "virtualbox_version_file") - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if b.config.VBoxVersionFile != ".vbox_version" { - t.Fatalf("bad value: %s", b.config.VBoxVersionFile) - } - - // Test with a good one - config["virtualbox_version_file"] = "foo" - b = Builder{} - warns, err = b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - if b.config.VBoxVersionFile != "foo" { - t.Fatalf("bad value: %s", b.config.VBoxVersionFile) - } -} diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index a8cb3b903..cecad072b 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -71,8 +71,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), SSHWaitTimeout: b.config.SSHWaitTimeout, }, + &vboxcommon.StepUploadVersion{ + Path: b.config.VBoxVersionFile, + }, /* - new(stepUploadVersion), new(stepUploadGuestAdditions), */ new(common.StepProvision), diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 2c2442bc8..00ee33181 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -16,6 +16,7 @@ type Config struct { vboxcommon.SSHConfig `mapstructure:",squash"` vboxcommon.ShutdownConfig `mapstructure:",squash"` vboxcommon.VBoxManageConfig `mapstructure:",squash"` + vboxcommon.VBoxVersionConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate } @@ -41,6 +42,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.RunConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.SSHConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(c.tpl)...) + errs = packer.MultiErrorAppend(errs, c.VBoxVersionConfig.Prepare(c.tpl)...) // Warnings var warnings []string From e51dde13ced40c9e39645df38d655b34ff3b30e7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 11:55:01 -0800 Subject: [PATCH 19/30] builder/virtualbox: tests for StepUploadVerison --- .../common/step_upload_version_test.go | 61 +++++++++++++++++++ builder/virtualbox/iso/builder.go | 16 ++--- builder/virtualbox/ovf/config.go | 16 ++--- 3 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 builder/virtualbox/common/step_upload_version_test.go diff --git a/builder/virtualbox/common/step_upload_version_test.go b/builder/virtualbox/common/step_upload_version_test.go new file mode 100644 index 000000000..234c4b5df --- /dev/null +++ b/builder/virtualbox/common/step_upload_version_test.go @@ -0,0 +1,61 @@ +package common + +import ( + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" + "testing" +) + +func TestStepUploadVersion_impl(t *testing.T) { + var _ multistep.Step = new(StepUploadVersion) +} + +func TestStepUploadVersion(t *testing.T) { + state := testState(t) + step := new(StepUploadVersion) + step.Path = "foopath" + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + + driver := state.Get("driver").(*DriverMock) + driver.VersionResult = "foo" + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Verify + if comm.UploadPath != "foopath" { + t.Fatalf("bad: %#v", comm.UploadPath) + } + if comm.UploadData != "foo" { + t.Fatalf("upload data bad: %#v", comm.UploadData) + } +} + +func TestStepUploadVersion_noPath(t *testing.T) { + state := testState(t) + step := new(StepUploadVersion) + step.Path = "" + + comm := new(packer.MockCommunicator) + state.Put("communicator", comm) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Verify + if comm.UploadCalled { + t.Fatal("bad") + } +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index d5402087c..6503ebeac 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -27,14 +27,14 @@ type Builder struct { } type config struct { - common.PackerConfig `mapstructure:",squash"` - vboxcommon.ExportConfig `mapstructure:",squash"` - vboxcommon.FloppyConfig `mapstructure:",squash"` - vboxcommon.OutputConfig `mapstructure:",squash"` - vboxcommon.RunConfig `mapstructure:",squash"` - vboxcommon.ShutdownConfig `mapstructure:",squash"` - vboxcommon.SSHConfig `mapstructure:",squash"` - vboxcommon.VBoxManageConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + vboxcommon.ExportConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.RunConfig `mapstructure:",squash"` + vboxcommon.ShutdownConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` + vboxcommon.VBoxManageConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"` BootCommand []string `mapstructure:"boot_command"` diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 00ee33181..ecd64ebd1 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -8,14 +8,14 @@ import ( // Config is the configuration structure for the builder. type Config struct { - common.PackerConfig `mapstructure:",squash"` - vboxcommon.ExportConfig `mapstructure:",squash"` - vboxcommon.FloppyConfig `mapstructure:",squash"` - vboxcommon.OutputConfig `mapstructure:",squash"` - vboxcommon.RunConfig `mapstructure:",squash"` - vboxcommon.SSHConfig `mapstructure:",squash"` - vboxcommon.ShutdownConfig `mapstructure:",squash"` - vboxcommon.VBoxManageConfig `mapstructure:",squash"` + common.PackerConfig `mapstructure:",squash"` + vboxcommon.ExportConfig `mapstructure:",squash"` + vboxcommon.FloppyConfig `mapstructure:",squash"` + vboxcommon.OutputConfig `mapstructure:",squash"` + vboxcommon.RunConfig `mapstructure:",squash"` + vboxcommon.SSHConfig `mapstructure:",squash"` + vboxcommon.ShutdownConfig `mapstructure:",squash"` + vboxcommon.VBoxManageConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"` tpl *packer.ConfigTemplate From 0de7bb33cdef3dbbcea737e8224a09e23f7318e5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 11:57:05 -0800 Subject: [PATCH 20/30] builder/virtualbox: set Tpl on VBoxManage --- builder/virtualbox/iso/builder.go | 1 + builder/virtualbox/ovf/builder.go | 1 + 2 files changed, 2 insertions(+) diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 6503ebeac..fc5ed095b 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -285,6 +285,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &vboxcommon.StepVBoxManage{ Commands: b.config.VBoxManage, + Tpl: b.config.tpl, }, &vboxcommon.StepRun{ BootWait: b.config.BootWait, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index cecad072b..c79125327 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -61,6 +61,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &vboxcommon.StepVBoxManage{ Commands: b.config.VBoxManage, + Tpl: b.config.tpl, }, &vboxcommon.StepRun{ BootWait: b.config.BootWait, From dd767c9d546a6c8123a0d828594e344488001dc4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:19:19 -0800 Subject: [PATCH 21/30] builder/virtualbox/ovf: StepImport to import an OVF --- builder/virtualbox/common/driver.go | 6 +++ builder/virtualbox/common/driver_4_2.go | 14 ++++++ builder/virtualbox/common/driver_mock.go | 22 +++++++++ builder/virtualbox/ovf/builder.go | 6 +-- builder/virtualbox/ovf/config.go | 2 + builder/virtualbox/ovf/step_import.go | 47 ++++++++++++++++++ builder/virtualbox/ovf/step_import_test.go | 55 ++++++++++++++++++++++ builder/virtualbox/ovf/step_test.go | 19 ++++++++ 8 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 builder/virtualbox/ovf/step_import.go create mode 100644 builder/virtualbox/ovf/step_import_test.go create mode 100644 builder/virtualbox/ovf/step_test.go diff --git a/builder/virtualbox/common/driver.go b/builder/virtualbox/common/driver.go index 75b259ac3..3fd8c89ab 100644 --- a/builder/virtualbox/common/driver.go +++ b/builder/virtualbox/common/driver.go @@ -19,6 +19,12 @@ type Driver interface { // Create a SATA controller. CreateSATAController(vm string, controller string) error + // Delete a VM by name + Delete(string) error + + // Import a VM + Import(string, string) error + // Checks if the VM with the given name is running. IsRunning(string) (bool, error) diff --git a/builder/virtualbox/common/driver_4_2.go b/builder/virtualbox/common/driver_4_2.go index 9b937faf9..78c91be5d 100644 --- a/builder/virtualbox/common/driver_4_2.go +++ b/builder/virtualbox/common/driver_4_2.go @@ -36,6 +36,20 @@ func (d *VBox42Driver) CreateSATAController(vmName string, name string) error { return d.VBoxManage(command...) } +func (d *VBox42Driver) Delete(name string) error { + return d.VBoxManage("unregistervm", name, "--delete") +} + +func (d *VBox42Driver) Import(name, path string) error { + args := []string{ + "import", path, + "--vsys", "0", + "--vmname", name, + } + + return d.VBoxManage(args...) +} + func (d *VBox42Driver) IsRunning(name string) (bool, error) { var stdout bytes.Buffer diff --git a/builder/virtualbox/common/driver_mock.go b/builder/virtualbox/common/driver_mock.go index 91b587070..b73fb5c5f 100644 --- a/builder/virtualbox/common/driver_mock.go +++ b/builder/virtualbox/common/driver_mock.go @@ -9,6 +9,15 @@ type DriverMock struct { CreateSATAControllerController string CreateSATAControllerErr error + DeleteCalled bool + DeleteName string + DeleteErr error + + ImportCalled bool + ImportName string + ImportPath string + ImportErr error + IsRunningName string IsRunningReturn bool IsRunningErr error @@ -36,6 +45,19 @@ func (d *DriverMock) CreateSATAController(vm string, controller string) error { return d.CreateSATAControllerErr } +func (d *DriverMock) Delete(name string) error { + d.DeleteCalled = true + d.DeleteName = name + return d.DeleteErr +} + +func (d *DriverMock) Import(name, path string) error { + d.ImportCalled = true + d.ImportName = name + d.ImportPath = path + return d.ImportErr +} + func (d *DriverMock) IsRunning(name string) (bool, error) { d.Lock() defer d.Unlock() diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index c79125327..1f50c4632 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -39,9 +39,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{ - /* - new(stepDownloadGuestAdditions), - */ + &StepImport{ + SourcePath: b.config.SourcePath, + }, &vboxcommon.StepOutputDir{ Force: b.config.PackerForce, Path: b.config.OutputDir, diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index ecd64ebd1..e1272f1ac 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -18,6 +18,8 @@ type Config struct { vboxcommon.VBoxManageConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"` + SourcePath string `mapstructure:"source_path"` + tpl *packer.ConfigTemplate } diff --git a/builder/virtualbox/ovf/step_import.go b/builder/virtualbox/ovf/step_import.go new file mode 100644 index 000000000..58d3983c1 --- /dev/null +++ b/builder/virtualbox/ovf/step_import.go @@ -0,0 +1,47 @@ +package ovf + +import ( + "fmt" + "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" + "github.com/mitchellh/packer/packer" +) + +// This step imports an OVF VM into VirtualBox. +type StepImport struct { + Name string + SourcePath string + + vmName string +} + +func (s *StepImport) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(vboxcommon.Driver) + ui := state.Get("ui").(packer.Ui) + + ui.Say(fmt.Sprintf("Importing VM: %s", s.SourcePath)) + if err := driver.Import(s.Name, s.SourcePath); err != nil { + err := fmt.Errorf("Error importing VM: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + s.vmName = s.Name + state.Put("vmName", s.Name) + return multistep.ActionContinue +} + +func (s *StepImport) Cleanup(state multistep.StateBag) { + if s.vmName == "" { + return + } + + driver := state.Get("driver").(vboxcommon.Driver) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Unregistering and deleting imported VM...") + if err := driver.Delete(s.vmName); err != nil { + ui.Error(fmt.Sprintf("Error deleting VM: %s", err)) + } +} diff --git a/builder/virtualbox/ovf/step_import_test.go b/builder/virtualbox/ovf/step_import_test.go new file mode 100644 index 000000000..e31d53791 --- /dev/null +++ b/builder/virtualbox/ovf/step_import_test.go @@ -0,0 +1,55 @@ +package ovf + +import ( + "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" + "testing" +) + +func TestStepImport_impl(t *testing.T) { + var _ multistep.Step = new(StepImport) +} + +func TestStepImport(t *testing.T) { + state := testState(t) + step := new(StepImport) + step.Name = "bar" + step.SourcePath = "foo" + + driver := state.Get("driver").(*vboxcommon.DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + + // Test driver + if !driver.ImportCalled { + t.Fatal("import should be called") + } + if driver.ImportName != step.Name { + t.Fatalf("bad: %#v", driver.ImportName) + } + if driver.ImportPath != step.SourcePath { + t.Fatalf("bad: %#v", driver.ImportPath) + } + + // Test output state + if name, ok := state.GetOk("vmName"); !ok { + t.Fatal("vmName should be set") + } else if name != "bar" { + t.Fatalf("bad: %#v", name) + } + + // Test cleanup + step.Cleanup(state) + if !driver.DeleteCalled { + t.Fatal("delete should be called") + } + if driver.DeleteName != "bar" { + t.Fatalf("bad: %#v", driver.DeleteName) + } +} diff --git a/builder/virtualbox/ovf/step_test.go b/builder/virtualbox/ovf/step_test.go new file mode 100644 index 000000000..d53e29154 --- /dev/null +++ b/builder/virtualbox/ovf/step_test.go @@ -0,0 +1,19 @@ +package ovf + +import ( + "bytes" + "github.com/mitchellh/multistep" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" + "github.com/mitchellh/packer/packer" + "testing" +) + +func testState(t *testing.T) multistep.StateBag { + state := new(multistep.BasicStateBag) + state.Put("driver", new(vboxcommon.DriverMock)) + state.Put("ui", &packer.BasicUi{ + Reader: new(bytes.Buffer), + Writer: new(bytes.Buffer), + }) + return state +} From 5b223f0798338254da15fc735e27e8956b63901d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:20:49 -0800 Subject: [PATCH 22/30] builder/virtualbox/ovf: re-order some steps --- builder/virtualbox/ovf/builder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 1f50c4632..22934048d 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -39,9 +39,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps. steps := []multistep.Step{ - &StepImport{ - SourcePath: b.config.SourcePath, - }, &vboxcommon.StepOutputDir{ Force: b.config.PackerForce, Path: b.config.OutputDir, @@ -50,6 +47,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &common.StepCreateFloppy{ Files: b.config.FloppyFiles, }, + &StepImport{ + SourcePath: b.config.SourcePath, + }, /* new(stepAttachGuestAdditions), */ From e69bb1858c2ded7c6de1db70f422befc50d267b3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:24:24 -0800 Subject: [PATCH 23/30] builder/virtualbox/ovf: specify name for import --- builder/virtualbox/ovf/builder.go | 1 + builder/virtualbox/ovf/config.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 22934048d..fffd90530 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -48,6 +48,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Files: b.config.FloppyFiles, }, &StepImport{ + Name: b.config.VMName, SourcePath: b.config.SourcePath, }, /* diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index e1272f1ac..853b81333 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -1,6 +1,7 @@ package ovf import ( + "fmt" vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" @@ -19,6 +20,7 @@ type Config struct { vboxcommon.VBoxVersionConfig `mapstructure:",squash"` SourcePath string `mapstructure:"source_path"` + VMName string `mapstructure:"vm_name"` tpl *packer.ConfigTemplate } @@ -36,6 +38,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } c.tpl.UserVars = c.PackerUserVars + // Defaults + if c.VMName == "" { + c.VMName = fmt.Sprintf("packer-%s-{{timestamp}}", c.PackerBuildName) + } + // Prepare the errors errs := common.CheckUnusedConfig(md) errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(c.tpl)...) @@ -46,6 +53,19 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.VBoxManageConfig.Prepare(c.tpl)...) errs = packer.MultiErrorAppend(errs, c.VBoxVersionConfig.Prepare(c.tpl)...) + templates := map[string]*string{ + "vm_name": &c.VMName, + } + + for n, ptr := range templates { + var err error + *ptr, err = c.tpl.Process(*ptr, nil) + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Error processing %s: %s", n, err)) + } + } + // Warnings var warnings []string if c.ShutdownCommand == "" { From dc2117125dbfad7e75ba1cafde0ab19811c5185e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:44:43 -0800 Subject: [PATCH 24/30] website: document the virtualbox-ovf builder --- .../builders/virtualbox-iso.html.markdown | 305 +++++++++++++++++ .../builders/virtualbox-ovf.html.markdown | 193 +++++++++++ .../docs/builders/virtualbox.html.markdown | 310 +----------------- 3 files changed, 514 insertions(+), 294 deletions(-) create mode 100644 website/source/docs/builders/virtualbox-iso.html.markdown create mode 100644 website/source/docs/builders/virtualbox-ovf.html.markdown diff --git a/website/source/docs/builders/virtualbox-iso.html.markdown b/website/source/docs/builders/virtualbox-iso.html.markdown new file mode 100644 index 000000000..668b1329d --- /dev/null +++ b/website/source/docs/builders/virtualbox-iso.html.markdown @@ -0,0 +1,305 @@ +--- +layout: "docs" +page_title: "VirtualBox Builder (from an ISO)" +--- + +# VirtualBox Builder (from an ISO) + +Type: `virtualbox` + +The VirtualBox builder is able to create [VirtualBox](https://www.virtualbox.org/) +virtual machines and export them in the OVF format, starting from an +ISO image. + +The builder builds a virtual machine by creating a new virtual machine +from scratch, booting it, installing an OS, provisioning software within +the OS, then shutting it down. The result of the VirtualBox builder is a directory +containing all the files necessary to run the virtual machine portably. + +## Basic Example + +Here is a basic example. This example is not functional. It will start the +OS installer but then fail because we don't provide the preseed file for +Ubuntu to self-install. Still, the example serves to show the basic configuration: + +
+{
+  "type": "virtualbox",
+  "guest_os_type": "Ubuntu_64",
+  "iso_url": "http://releases.ubuntu.com/12.04/ubuntu-12.04.3-server-amd64.iso",
+  "iso_checksum": "2cbe868812a871242cdcdd8f2fd6feb9",
+  "iso_checksum_type": "md5",
+  "ssh_username": "packer",
+  "ssh_password": "packer",
+  "ssh_wait_timeout": "30s",
+  "shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
+}
+
+ +It is important to add a `shutdown_command`. By default Packer halts the +virtual machine and the file system may not be sync'd. Thus, changes made in a +provisioner might not be saved. + +## Configuration Reference + +There are many configuration options available for the VirtualBox builder. +They are organized below into two categories: required and optional. Within +each category, the available options are alphabetized and described. + +Required: + +* `iso_checksum` (string) - The checksum for the OS ISO file. Because ISO + files are so large, this is required and Packer will verify it prior + to booting a virtual machine with the ISO attached. The type of the + checksum is specified with `iso_checksum_type`, documented below. + +* `iso_checksum_type` (string) - The type of the checksum specified in + `iso_checksum`. Valid values are "md5", "sha1", "sha256", or "sha512" currently. + +* `iso_url` (string) - A URL to the ISO containing the installation image. + This URL can be either an HTTP URL or a file URL (or path to a file). + If this is an HTTP URL, Packer will download it and cache it between + runs. + +* `ssh_username` (string) - The username to use to SSH into the machine + once the OS is installed. + +Optional: + +* `boot_command` (array of strings) - This is an array of commands to type + when the virtual machine is first booted. The goal of these commands should + be to type just enough to initialize the operating system installer. Special + keys can be typed as well, and are covered in the section below on the boot + command. If this is not specified, it is assumed the installer will start + itself. + +* `boot_wait` (string) - The time to wait after booting the initial virtual + machine before typing the `boot_command`. The value of this should be + a duration. Examples are "5s" and "1m30s" which will cause Packer to wait + five seconds and one minute 30 seconds, respectively. If this isn't specified, + the default is 10 seconds. + +* `disk_size` (int) - The size, in megabytes, of the hard disk to create + for the VM. By default, this is 40000 (40 GB). + +* `floppy_files` (array of strings) - A list of files to put onto a floppy + disk that is attached when the VM is booted for the first time. This is + most useful for unattended Windows installs, which look for an + `Autounattend.xml` file on removable media. By default no floppy will + be attached. The files listed in this configuration will all be put + into the root directory of the floppy disk; sub-directories are not supported. + +* `format` (string) - Either "ovf" or "ova", this specifies the output + format of the exported virtual machine. This defaults to "ovf". + +* `guest_additions_mode` (string) - The method by which guest additions + are made available to the guest for installation. Valid options are + "upload", "attach", or "disable". The functions of each of these should be + self-explanatory. The default value is "upload". If "disable" is used, + guest additions won't be downloaded, either. + +* `guest_additions_path` (string) - The path on the guest virtual machine + where the VirtualBox guest additions ISO will be uploaded. By default this + is "VBoxGuestAdditions.iso" which should upload into the login directory + of the user. This is a [configuration template](/docs/templates/configuration-templates.html) + where the `Version` variable is replaced with the VirtualBox version. + +* `guest_additions_sha256` (string) - The SHA256 checksum of the guest + additions ISO that will be uploaded to the guest VM. By default the + checksums will be downloaded from the VirtualBox website, so this only + needs to be set if you want to be explicit about the checksum. + +* `guest_additions_url` (string) - The URL to the guest additions ISO + to upload. This can also be a file URL if the ISO is at a local path. + By default the VirtualBox builder will go and download the proper + guest additions ISO from the internet. + +* `guest_os_type` (string) - The guest OS type being installed. By default + this is "other", but you can get _dramatic_ performance improvements by + setting this to the proper value. To view all available values for this + run `VBoxManage list ostypes`. Setting the correct value hints to VirtualBox + how to optimize the virtual hardware to work best with that operating + system. + +* `hard_drive_interface` (string) - The type of controller that the primary + hard drive is attached to, defaults to "ide". When set to "sata", the + drive is attached to an AHCI SATA controller. + +* `headless` (bool) - Packer defaults to building VirtualBox + virtual machines by launching a GUI that shows the console of the + machine being built. When this value is set to true, the machine will + start without a console. + +* `http_directory` (string) - Path to a directory to serve using an HTTP + server. The files in this directory will be available over HTTP that will + be requestable from the virtual machine. This is useful for hosting + kickstart files and so on. By default this is "", which means no HTTP + server will be started. The address and port of the HTTP server will be + available as variables in `boot_command`. This is covered in more detail + below. + +* `http_port_min` and `http_port_max` (int) - These are the minimum and + maximum port to use for the HTTP server started to serve the `http_directory`. + Because Packer often runs in parallel, Packer will choose a randomly available + port in this range to run the HTTP server. If you want to force the HTTP + server to be on one port, make this minimum and maximum port the same. + By default the values are 8000 and 9000, respectively. + +* `iso_urls` (array of strings) - Multiple URLs for the ISO to download. + Packer will try these in order. If anything goes wrong attempting to download + or while downloading a single URL, it will move on to the next. All URLs + must point to the same file (same checksum). By default this is empty + and `iso_url` is used. Only one of `iso_url` or `iso_urls` can be specified. + +* `output_directory` (string) - This is the path to the directory where the + resulting virtual machine will be created. This may be relative or absolute. + If relative, the path is relative to the working directory when `packer` + is executed. This directory must not exist or be empty prior to running the builder. + By default this is "output-BUILDNAME" where "BUILDNAME" is the name + of the build. + +* `shutdown_command` (string) - The command to use to gracefully shut down + the machine once all the provisioning is done. By default this is an empty + string, which tells Packer to just forcefully shut down the machine. + +* `shutdown_timeout` (string) - The amount of time to wait after executing + the `shutdown_command` for the virtual machine to actually shut down. + If it doesn't shut down in this time, it is an error. By default, the timeout + is "5m", or five minutes. + +* `ssh_host_port_min` and `ssh_host_port_max` (uint) - The minimum and + maximum port to use for the SSH port on the host machine which is forwarded + to the SSH port on the guest machine. Because Packer often runs in parallel, + Packer will choose a randomly available port in this range to use as the + host port. + +* `ssh_key_path` (string) - Path to a private key to use for authenticating + with SSH. By default this is not set (key-based auth won't be used). + The associated public key is expected to already be configured on the + VM being prepared by some other process (kickstart, etc.). + +* `ssh_password` (string) - The password for `ssh_username` to use to + authenticate with SSH. By default this is the empty string. + +* `ssh_port` (int) - The port that SSH will be listening on in the guest + virtual machine. By default this is 22. + +* `ssh_wait_timeout` (string) - The duration to wait for SSH to become + available. By default this is "20m", or 20 minutes. Note that this should + be quite long since the timer begins as soon as the virtual machine is booted. + +* `vboxmanage` (array of array of strings) - Custom `VBoxManage` commands to + execute in order to further customize the virtual machine being created. + The value of this is an array of commands to execute. The commands are executed + in the order defined in the template. For each command, the command is + defined itself as an array of strings, where each string represents a single + argument on the command-line to `VBoxManage` (but excluding `VBoxManage` + itself). Each arg is treated as a [configuration template](/docs/templates/configuration-templates.html), + where the `Name` variable is replaced with the VM name. More details on how + to use `VBoxManage` are below. + +* `virtualbox_version_file` (string) - The path within the virtual machine + to upload a file that contains the VirtualBox version that was used to + create the machine. This information can be useful for provisioning. + By default this is ".vbox_version", which will generally upload it into + the home directory. + +* `vm_name` (string) - This is the name of the OVF file for the new virtual + machine, without the file extension. By default this is "packer-BUILDNAME", + where "BUILDNAME" is the name of the build. + +## Boot Command + +The `boot_command` configuration is very important: it specifies the keys +to type when the virtual machine is first booted in order to start the +OS installer. This command is typed after `boot_wait`, which gives the +virtual machine some time to actually load the ISO. + +As documented above, the `boot_command` is an array of strings. The +strings are all typed in sequence. It is an array only to improve readability +within the template. + +The boot command is "typed" character for character over a VNC connection +to the machine, simulating a human actually typing the keyboard. There are +a set of special keys available. If these are in your boot command, they +will be replaced by the proper key: + +* `` and `` - Simulates an actual "enter" or "return" keypress. + +* `` - Simulates pressing the escape key. + +* `` - Simulates pressing the tab key. + +* `` `` `` - Adds a 1, 5 or 10 second pause before sending any additional keys. This + is useful if you have to generally wait for the UI to update before typing more. + +In addition to the special keys, each command to type is treated as a +[configuration template](/docs/templates/configuration-templates.html). +The available variables are: + +* `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server + that is started serving the directory specified by the `http_directory` + configuration parameter. If `http_directory` isn't specified, these will + be blank! + +Example boot command. This is actually a working boot command used to start +an Ubuntu 12.04 installer: + +
+[
+  "<esc><esc><enter><wait>",
+  "/install/vmlinuz noapic ",
+  "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
+  "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
+  "hostname={{ .Name }} ",
+  "fb=false debconf/frontend=noninteractive ",
+  "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
+  "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
+  "initrd=/install/initrd.gz -- <enter>"
+]
+
+ +## Guest Additions + +Packer will automatically download the proper guest additions for the +version of VirtualBox that is running and upload those guest additions into +the virtual machine so that provisioners can easily install them. + +Packer downloads the guest additions from the official VirtualBox website, +and verifies the file with the official checksums released by VirtualBox. + +After the virtual machine is up and the operating system is installed, +Packer uploads the guest additions into the virtual machine. The path where +they are uploaded is controllable by `guest_additions_path`, and defaults +to "VBoxGuestAdditions.iso". Without an absolute path, it is uploaded to the +home directory of the SSH user. + +## VBoxManage Commands + +In order to perform extra customization of the virtual machine, a template +can define extra calls to `VBoxManage` to perform. [VBoxManage](http://www.virtualbox.org/manual/ch08.html) +is the command-line interface to VirtualBox where you can completely control +VirtualBox. It can be used to do things such as set RAM, CPUs, etc. + +Extra VBoxManage commands are defined in the template in the `vboxmanage` section. +An example is shown below that sets the memory and number of CPUs within the +virtual machine: + +
+{
+  "vboxmanage": [
+    ["modifyvm", "{{.Name}}", "--memory", "1024"],
+    ["modifyvm", "{{.Name}}", "--cpus", "2"]
+  ]
+}
+
+ +The value of `vboxmanage` is an array of commands to execute. These commands +are executed in the order defined. So in the above example, the memory will be +set followed by the CPUs. + +Each command itself is an array of strings, where each string is an argument +to `VBoxManage`. Each argument is treated as a +[configuration template](/docs/templates/configuration-templates.html). +The only available variable is `Name` which is replaced with the unique +name of the VM, which is required for many VBoxManage calls. diff --git a/website/source/docs/builders/virtualbox-ovf.html.markdown b/website/source/docs/builders/virtualbox-ovf.html.markdown new file mode 100644 index 000000000..36299e368 --- /dev/null +++ b/website/source/docs/builders/virtualbox-ovf.html.markdown @@ -0,0 +1,193 @@ +--- +layout: "docs" +page_title: "VirtualBox Builder (from an OVF/OVA)" +--- + +# VirtualBox Builder (from an OVF/OVA) + +Type: `virtualbox-ovf` + +This VirtualBox builder is able to create [VirtualBox](https://www.virtualbox.org/) +virtual machines and export them in the OVF format, starting from an +existing OVF/OVA (exported virtual machine image). + +The builder builds a virtual machine by importing an existing OVF or OVA +file. It then boots this image, runs provisioners on this new VM, and +exports that VM to create the image. The imported machine is deleted prior +to finishing the build. + +## Basic Example + +Here is a basic example. This example is functional if you have an OVF matching +the settings here. + +
+{
+  "type": "virtualbox-ovf",
+  "source_path": "source.ovf",
+  "ssh_username": "packer",
+  "ssh_password": "packer",
+  "ssh_wait_timeout": "30s",
+  "shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
+}
+
+ +It is important to add a `shutdown_command`. By default Packer halts the +virtual machine and the file system may not be sync'd. Thus, changes made in a +provisioner might not be saved. + +## Configuration Reference + +There are many configuration options available for the VirtualBox builder. +They are organized below into two categories: required and optional. Within +each category, the available options are alphabetized and described. + +Required: + +* `source_path` (string) - The path to an OVF or OVA file that acts as + the source of this build. + +* `ssh_username` (string) - The username to use to SSH into the machine + once the OS is installed. + +Optional: + +* `floppy_files` (array of strings) - A list of files to put onto a floppy + disk that is attached when the VM is booted for the first time. This is + most useful for unattended Windows installs, which look for an + `Autounattend.xml` file on removable media. By default no floppy will + be attached. The files listed in this configuration will all be put + into the root directory of the floppy disk; sub-directories are not supported. + +* `format` (string) - Either "ovf" or "ova", this specifies the output + format of the exported virtual machine. This defaults to "ovf". + +* `guest_additions_mode` (string) - The method by which guest additions + are made available to the guest for installation. Valid options are + "upload", "attach", or "disable". The functions of each of these should be + self-explanatory. The default value is "upload". If "disable" is used, + guest additions won't be downloaded, either. + +* `guest_additions_path` (string) - The path on the guest virtual machine + where the VirtualBox guest additions ISO will be uploaded. By default this + is "VBoxGuestAdditions.iso" which should upload into the login directory + of the user. This is a [configuration template](/docs/templates/configuration-templates.html) + where the `Version` variable is replaced with the VirtualBox version. + +* `guest_additions_sha256` (string) - The SHA256 checksum of the guest + additions ISO that will be uploaded to the guest VM. By default the + checksums will be downloaded from the VirtualBox website, so this only + needs to be set if you want to be explicit about the checksum. + +* `guest_additions_url` (string) - The URL to the guest additions ISO + to upload. This can also be a file URL if the ISO is at a local path. + By default the VirtualBox builder will go and download the proper + guest additions ISO from the internet. + +* `headless` (bool) - Packer defaults to building VirtualBox + virtual machines by launching a GUI that shows the console of the + machine being built. When this value is set to true, the machine will + start without a console. + +* `output_directory` (string) - This is the path to the directory where the + resulting virtual machine will be created. This may be relative or absolute. + If relative, the path is relative to the working directory when `packer` + is executed. This directory must not exist or be empty prior to running the builder. + By default this is "output-BUILDNAME" where "BUILDNAME" is the name + of the build. + +* `shutdown_command` (string) - The command to use to gracefully shut down + the machine once all the provisioning is done. By default this is an empty + string, which tells Packer to just forcefully shut down the machine. + +* `shutdown_timeout` (string) - The amount of time to wait after executing + the `shutdown_command` for the virtual machine to actually shut down. + If it doesn't shut down in this time, it is an error. By default, the timeout + is "5m", or five minutes. + +* `ssh_host_port_min` and `ssh_host_port_max` (uint) - The minimum and + maximum port to use for the SSH port on the host machine which is forwarded + to the SSH port on the guest machine. Because Packer often runs in parallel, + Packer will choose a randomly available port in this range to use as the + host port. + +* `ssh_key_path` (string) - Path to a private key to use for authenticating + with SSH. By default this is not set (key-based auth won't be used). + The associated public key is expected to already be configured on the + VM being prepared by some other process (kickstart, etc.). + +* `ssh_password` (string) - The password for `ssh_username` to use to + authenticate with SSH. By default this is the empty string. + +* `ssh_port` (int) - The port that SSH will be listening on in the guest + virtual machine. By default this is 22. + +* `ssh_wait_timeout` (string) - The duration to wait for SSH to become + available. By default this is "20m", or 20 minutes. Note that this should + be quite long since the timer begins as soon as the virtual machine is booted. + +* `vboxmanage` (array of array of strings) - Custom `VBoxManage` commands to + execute in order to further customize the virtual machine being created. + The value of this is an array of commands to execute. The commands are executed + in the order defined in the template. For each command, the command is + defined itself as an array of strings, where each string represents a single + argument on the command-line to `VBoxManage` (but excluding `VBoxManage` + itself). Each arg is treated as a [configuration template](/docs/templates/configuration-templates.html), + where the `Name` variable is replaced with the VM name. More details on how + to use `VBoxManage` are below. + +* `virtualbox_version_file` (string) - The path within the virtual machine + to upload a file that contains the VirtualBox version that was used to + create the machine. This information can be useful for provisioning. + By default this is ".vbox_version", which will generally upload it into + the home directory. + +* `vm_name` (string) - This is the name of the virtual machine when it is + imported as well as the name of the OVF file when the virtual machine is + exported. By default this is "packer-BUILDNAME", where "BUILDNAME" is + the name of the build. + +## Guest Additions + +Packer will automatically download the proper guest additions for the +version of VirtualBox that is running and upload those guest additions into +the virtual machine so that provisioners can easily install them. + +Packer downloads the guest additions from the official VirtualBox website, +and verifies the file with the official checksums released by VirtualBox. + +After the virtual machine is up and the operating system is installed, +Packer uploads the guest additions into the virtual machine. The path where +they are uploaded is controllable by `guest_additions_path`, and defaults +to "VBoxGuestAdditions.iso". Without an absolute path, it is uploaded to the +home directory of the SSH user. + +## VBoxManage Commands + +In order to perform extra customization of the virtual machine, a template +can define extra calls to `VBoxManage` to perform. [VBoxManage](http://www.virtualbox.org/manual/ch08.html) +is the command-line interface to VirtualBox where you can completely control +VirtualBox. It can be used to do things such as set RAM, CPUs, etc. + +Extra VBoxManage commands are defined in the template in the `vboxmanage` section. +An example is shown below that sets the memory and number of CPUs within the +virtual machine: + +
+{
+  "vboxmanage": [
+    ["modifyvm", "{{.Name}}", "--memory", "1024"],
+    ["modifyvm", "{{.Name}}", "--cpus", "2"]
+  ]
+}
+
+ +The value of `vboxmanage` is an array of commands to execute. These commands +are executed in the order defined. So in the above example, the memory will be +set followed by the CPUs. + +Each command itself is an array of strings, where each string is an argument +to `VBoxManage`. Each argument is treated as a +[configuration template](/docs/templates/configuration-templates.html). +The only available variable is `Name` which is replaced with the unique +name of the VM, which is required for many VBoxManage calls. diff --git a/website/source/docs/builders/virtualbox.html.markdown b/website/source/docs/builders/virtualbox.html.markdown index b73630358..2e587588b 100644 --- a/website/source/docs/builders/virtualbox.html.markdown +++ b/website/source/docs/builders/virtualbox.html.markdown @@ -1,303 +1,25 @@ --- layout: "docs" +page_title: "Amazon AMI Builder" --- # VirtualBox Builder -Type: `virtualbox` +The VirtualBox builder is able to create [VirtualBox](http://www.virtualbox.org) +virtual machines and export them in the OVA or OVF format. -The VirtualBox builder is able to create [VirtualBox](https://www.virtualbox.org/) -virtual machines and export them in the OVF format. +Packer actually comes with multiple builders able to create VirtualBox +machines, depending on the strategy you want to use to build the image. +Packer supports the following VirtualBox builders: -The builder builds a virtual machine by creating a new virtual machine -from scratch, booting it, installing an OS, provisioning software within -the OS, then shutting it down. The result of the VirtualBox builder is a directory -containing all the files necessary to run the virtual machine portably. +* [virtualbox-iso](/docs/builders/virtualbox-iso.html) - Starts from + an ISO file, creates a brand new VirtualBox VM, installs an OS, + provisions software within the OS, then exports that machine to create + an image. This is best for people who want to start from scratch. -## Basic Example - -Here is a basic example. This example is not functional. It will start the -OS installer but then fail because we don't provide the preseed file for -Ubuntu to self-install. Still, the example serves to show the basic configuration: - -
-{
-  "type": "virtualbox",
-  "guest_os_type": "Ubuntu_64",
-  "iso_url": "http://releases.ubuntu.com/12.04/ubuntu-12.04.3-server-amd64.iso",
-  "iso_checksum": "2cbe868812a871242cdcdd8f2fd6feb9",
-  "iso_checksum_type": "md5",
-  "ssh_username": "packer",
-  "ssh_password": "packer",
-  "ssh_wait_timeout": "30s",
-  "shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
-}
-
- -It is important to add a `shutdown_command`. By default Packer halts the -virtual machine and the file system may not be sync'd. Thus, changes made in a -provisioner might not be saved. - -## Configuration Reference - -There are many configuration options available for the VirtualBox builder. -They are organized below into two categories: required and optional. Within -each category, the available options are alphabetized and described. - -Required: - -* `iso_checksum` (string) - The checksum for the OS ISO file. Because ISO - files are so large, this is required and Packer will verify it prior - to booting a virtual machine with the ISO attached. The type of the - checksum is specified with `iso_checksum_type`, documented below. - -* `iso_checksum_type` (string) - The type of the checksum specified in - `iso_checksum`. Valid values are "md5", "sha1", "sha256", or "sha512" currently. - -* `iso_url` (string) - A URL to the ISO containing the installation image. - This URL can be either an HTTP URL or a file URL (or path to a file). - If this is an HTTP URL, Packer will download it and cache it between - runs. - -* `ssh_username` (string) - The username to use to SSH into the machine - once the OS is installed. - -Optional: - -* `boot_command` (array of strings) - This is an array of commands to type - when the virtual machine is first booted. The goal of these commands should - be to type just enough to initialize the operating system installer. Special - keys can be typed as well, and are covered in the section below on the boot - command. If this is not specified, it is assumed the installer will start - itself. - -* `boot_wait` (string) - The time to wait after booting the initial virtual - machine before typing the `boot_command`. The value of this should be - a duration. Examples are "5s" and "1m30s" which will cause Packer to wait - five seconds and one minute 30 seconds, respectively. If this isn't specified, - the default is 10 seconds. - -* `disk_size` (int) - The size, in megabytes, of the hard disk to create - for the VM. By default, this is 40000 (40 GB). - -* `floppy_files` (array of strings) - A list of files to put onto a floppy - disk that is attached when the VM is booted for the first time. This is - most useful for unattended Windows installs, which look for an - `Autounattend.xml` file on removable media. By default no floppy will - be attached. The files listed in this configuration will all be put - into the root directory of the floppy disk; sub-directories are not supported. - -* `format` (string) - Either "ovf" or "ova", this specifies the output - format of the exported virtual machine. This defaults to "ovf". - -* `guest_additions_mode` (string) - The method by which guest additions - are made available to the guest for installation. Valid options are - "upload", "attach", or "disable". The functions of each of these should be - self-explanatory. The default value is "upload". If "disable" is used, - guest additions won't be downloaded, either. - -* `guest_additions_path` (string) - The path on the guest virtual machine - where the VirtualBox guest additions ISO will be uploaded. By default this - is "VBoxGuestAdditions.iso" which should upload into the login directory - of the user. This is a [configuration template](/docs/templates/configuration-templates.html) - where the `Version` variable is replaced with the VirtualBox version. - -* `guest_additions_sha256` (string) - The SHA256 checksum of the guest - additions ISO that will be uploaded to the guest VM. By default the - checksums will be downloaded from the VirtualBox website, so this only - needs to be set if you want to be explicit about the checksum. - -* `guest_additions_url` (string) - The URL to the guest additions ISO - to upload. This can also be a file URL if the ISO is at a local path. - By default the VirtualBox builder will go and download the proper - guest additions ISO from the internet. - -* `guest_os_type` (string) - The guest OS type being installed. By default - this is "other", but you can get _dramatic_ performance improvements by - setting this to the proper value. To view all available values for this - run `VBoxManage list ostypes`. Setting the correct value hints to VirtualBox - how to optimize the virtual hardware to work best with that operating - system. - -* `hard_drive_interface` (string) - The type of controller that the primary - hard drive is attached to, defaults to "ide". When set to "sata", the - drive is attached to an AHCI SATA controller. - -* `headless` (bool) - Packer defaults to building VirtualBox - virtual machines by launching a GUI that shows the console of the - machine being built. When this value is set to true, the machine will - start without a console. - -* `http_directory` (string) - Path to a directory to serve using an HTTP - server. The files in this directory will be available over HTTP that will - be requestable from the virtual machine. This is useful for hosting - kickstart files and so on. By default this is "", which means no HTTP - server will be started. The address and port of the HTTP server will be - available as variables in `boot_command`. This is covered in more detail - below. - -* `http_port_min` and `http_port_max` (int) - These are the minimum and - maximum port to use for the HTTP server started to serve the `http_directory`. - Because Packer often runs in parallel, Packer will choose a randomly available - port in this range to run the HTTP server. If you want to force the HTTP - server to be on one port, make this minimum and maximum port the same. - By default the values are 8000 and 9000, respectively. - -* `iso_urls` (array of strings) - Multiple URLs for the ISO to download. - Packer will try these in order. If anything goes wrong attempting to download - or while downloading a single URL, it will move on to the next. All URLs - must point to the same file (same checksum). By default this is empty - and `iso_url` is used. Only one of `iso_url` or `iso_urls` can be specified. - -* `output_directory` (string) - This is the path to the directory where the - resulting virtual machine will be created. This may be relative or absolute. - If relative, the path is relative to the working directory when `packer` - is executed. This directory must not exist or be empty prior to running the builder. - By default this is "output-BUILDNAME" where "BUILDNAME" is the name - of the build. - -* `shutdown_command` (string) - The command to use to gracefully shut down - the machine once all the provisioning is done. By default this is an empty - string, which tells Packer to just forcefully shut down the machine. - -* `shutdown_timeout` (string) - The amount of time to wait after executing - the `shutdown_command` for the virtual machine to actually shut down. - If it doesn't shut down in this time, it is an error. By default, the timeout - is "5m", or five minutes. - -* `ssh_host_port_min` and `ssh_host_port_max` (uint) - The minimum and - maximum port to use for the SSH port on the host machine which is forwarded - to the SSH port on the guest machine. Because Packer often runs in parallel, - Packer will choose a randomly available port in this range to use as the - host port. - -* `ssh_key_path` (string) - Path to a private key to use for authenticating - with SSH. By default this is not set (key-based auth won't be used). - The associated public key is expected to already be configured on the - VM being prepared by some other process (kickstart, etc.). - -* `ssh_password` (string) - The password for `ssh_username` to use to - authenticate with SSH. By default this is the empty string. - -* `ssh_port` (int) - The port that SSH will be listening on in the guest - virtual machine. By default this is 22. - -* `ssh_wait_timeout` (string) - The duration to wait for SSH to become - available. By default this is "20m", or 20 minutes. Note that this should - be quite long since the timer begins as soon as the virtual machine is booted. - -* `vboxmanage` (array of array of strings) - Custom `VBoxManage` commands to - execute in order to further customize the virtual machine being created. - The value of this is an array of commands to execute. The commands are executed - in the order defined in the template. For each command, the command is - defined itself as an array of strings, where each string represents a single - argument on the command-line to `VBoxManage` (but excluding `VBoxManage` - itself). Each arg is treated as a [configuration template](/docs/templates/configuration-templates.html), - where the `Name` variable is replaced with the VM name. More details on how - to use `VBoxManage` are below. - -* `virtualbox_version_file` (string) - The path within the virtual machine - to upload a file that contains the VirtualBox version that was used to - create the machine. This information can be useful for provisioning. - By default this is ".vbox_version", which will generally upload it into - the home directory. - -* `vm_name` (string) - This is the name of the OVF file for the new virtual - machine, without the file extension. By default this is "packer-BUILDNAME", - where "BUILDNAME" is the name of the build. - -## Boot Command - -The `boot_command` configuration is very important: it specifies the keys -to type when the virtual machine is first booted in order to start the -OS installer. This command is typed after `boot_wait`, which gives the -virtual machine some time to actually load the ISO. - -As documented above, the `boot_command` is an array of strings. The -strings are all typed in sequence. It is an array only to improve readability -within the template. - -The boot command is "typed" character for character over a VNC connection -to the machine, simulating a human actually typing the keyboard. There are -a set of special keys available. If these are in your boot command, they -will be replaced by the proper key: - -* `` and `` - Simulates an actual "enter" or "return" keypress. - -* `` - Simulates pressing the escape key. - -* `` - Simulates pressing the tab key. - -* `` `` `` - Adds a 1, 5 or 10 second pause before sending any additional keys. This - is useful if you have to generally wait for the UI to update before typing more. - -In addition to the special keys, each command to type is treated as a -[configuration template](/docs/templates/configuration-templates.html). -The available variables are: - -* `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server - that is started serving the directory specified by the `http_directory` - configuration parameter. If `http_directory` isn't specified, these will - be blank! - -Example boot command. This is actually a working boot command used to start -an Ubuntu 12.04 installer: - -
-[
-  "<esc><esc><enter><wait>",
-  "/install/vmlinuz noapic ",
-  "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
-  "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
-  "hostname={{ .Name }} ",
-  "fb=false debconf/frontend=noninteractive ",
-  "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
-  "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
-  "initrd=/install/initrd.gz -- <enter>"
-]
-
- -## Guest Additions - -Packer will automatically download the proper guest additions for the -version of VirtualBox that is running and upload those guest additions into -the virtual machine so that provisioners can easily install them. - -Packer downloads the guest additions from the official VirtualBox website, -and verifies the file with the official checksums released by VirtualBox. - -After the virtual machine is up and the operating system is installed, -Packer uploads the guest additions into the virtual machine. The path where -they are uploaded is controllable by `guest_additions_path`, and defaults -to "VBoxGuestAdditions.iso". Without an absolute path, it is uploaded to the -home directory of the SSH user. - -## VBoxManage Commands - -In order to perform extra customization of the virtual machine, a template -can define extra calls to `VBoxManage` to perform. [VBoxManage](http://www.virtualbox.org/manual/ch08.html) -is the command-line interface to VirtualBox where you can completely control -VirtualBox. It can be used to do things such as set RAM, CPUs, etc. - -Extra VBoxManage commands are defined in the template in the `vboxmanage` section. -An example is shown below that sets the memory and number of CPUs within the -virtual machine: - -
-{
-  "vboxmanage": [
-    ["modifyvm", "{{.Name}}", "--memory", "1024"],
-    ["modifyvm", "{{.Name}}", "--cpus", "2"]
-  ]
-}
-
- -The value of `vboxmanage` is an array of commands to execute. These commands -are executed in the order defined. So in the above example, the memory will be -set followed by the CPUs. - -Each command itself is an array of strings, where each string is an argument -to `VBoxManage`. Each argument is treated as a -[configuration template](/docs/templates/configuration-templates.html). -The only available variable is `Name` which is replaced with the unique -name of the VM, which is required for many VBoxManage calls. +* [virtualbox-ovf](/docs/builders/virtualbox-ovf.html) - This builder + imports an existing OVF/OVA file, runs provisioners on top of that VM, + and exports that machine to create an image. This is best if you have + an existing VirtualBox VM export you want to use as the source. As an + additional benefit, you can feed the artifact of this builder back into + itself to iterate on a machine. From eb542229cdb4fec67c1d8c9fba4b2c46b20030b5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:47:02 -0800 Subject: [PATCH 25/30] plugin/builder-virtualbox-ovf --- config.go | 1 + plugin/builder-virtualbox-ovf/main.go | 15 +++++++++++++++ plugin/builder-virtualbox-ovf/main_test.go | 1 + 3 files changed, 17 insertions(+) create mode 100644 plugin/builder-virtualbox-ovf/main.go create mode 100644 plugin/builder-virtualbox-ovf/main_test.go diff --git a/config.go b/config.go index 20954fd87..bf7c5fbbb 100644 --- a/config.go +++ b/config.go @@ -28,6 +28,7 @@ const defaultConfig = ` "openstack": "packer-builder-openstack", "qemu": "packer-builder-qemu", "virtualbox": "packer-builder-virtualbox", + "virtualbox-ovf": "packer-builder-virtualbox-ovf", "vmware": "packer-builder-vmware" }, diff --git a/plugin/builder-virtualbox-ovf/main.go b/plugin/builder-virtualbox-ovf/main.go new file mode 100644 index 000000000..e26825d1e --- /dev/null +++ b/plugin/builder-virtualbox-ovf/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/mitchellh/packer/builder/virtualbox/ovf" + "github.com/mitchellh/packer/packer/plugin" +) + +func main() { + server, err := plugin.Server() + if err != nil { + panic(err) + } + server.RegisterBuilder(new(ovf.Builder)) + server.Serve() +} diff --git a/plugin/builder-virtualbox-ovf/main_test.go b/plugin/builder-virtualbox-ovf/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/plugin/builder-virtualbox-ovf/main_test.go @@ -0,0 +1 @@ +package main From efac707078607f235a2fa8f73533e6d7c2ec222e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:52:23 -0800 Subject: [PATCH 26/30] Rename "virtualbox" builder to "virtualbox-iso" --- config.go | 2 +- plugin/{builder-virtualbox => builder-virtualbox-iso}/main.go | 0 .../{builder-virtualbox => builder-virtualbox-iso}/main_test.go | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename plugin/{builder-virtualbox => builder-virtualbox-iso}/main.go (100%) rename plugin/{builder-virtualbox => builder-virtualbox-iso}/main_test.go (100%) diff --git a/config.go b/config.go index bf7c5fbbb..15f3d764f 100644 --- a/config.go +++ b/config.go @@ -27,7 +27,7 @@ const defaultConfig = ` "googlecompute": "packer-builder-googlecompute", "openstack": "packer-builder-openstack", "qemu": "packer-builder-qemu", - "virtualbox": "packer-builder-virtualbox", + "virtualbox-iso": "packer-builder-virtualbox-iso", "virtualbox-ovf": "packer-builder-virtualbox-ovf", "vmware": "packer-builder-vmware" }, diff --git a/plugin/builder-virtualbox/main.go b/plugin/builder-virtualbox-iso/main.go similarity index 100% rename from plugin/builder-virtualbox/main.go rename to plugin/builder-virtualbox-iso/main.go diff --git a/plugin/builder-virtualbox/main_test.go b/plugin/builder-virtualbox-iso/main_test.go similarity index 100% rename from plugin/builder-virtualbox/main_test.go rename to plugin/builder-virtualbox-iso/main_test.go From 89ab009c3f7e2985844587a4992c12b22296c756 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 16:01:28 -0800 Subject: [PATCH 27/30] command/fix: add fixer to fix virtualbox to virtualbox-iso --- command/fix/fixer.go | 2 + command/fix/fixer_virtualbox_rename.go | 46 +++++++++++++++++++ command/fix/fixer_virtualbox_rename_test.go | 49 +++++++++++++++++++++ command/fix/help.go | 1 + 4 files changed, 98 insertions(+) create mode 100644 command/fix/fixer_virtualbox_rename.go create mode 100644 command/fix/fixer_virtualbox_rename_test.go diff --git a/command/fix/fixer.go b/command/fix/fixer.go index fd4d947da..b4dc2013c 100644 --- a/command/fix/fixer.go +++ b/command/fix/fixer.go @@ -24,6 +24,7 @@ func init() { "createtime": new(FixerCreateTime), "pp-vagrant-override": new(FixerVagrantPPOverride), "virtualbox-gaattach": new(FixerVirtualBoxGAAttach), + "virtualbox-rename": new(FixerVirtualBoxRename), } FixerOrder = []string{ @@ -31,5 +32,6 @@ func init() { "createtime", "virtualbox-gaattach", "pp-vagrant-override", + "virtualbox-rename", } } diff --git a/command/fix/fixer_virtualbox_rename.go b/command/fix/fixer_virtualbox_rename.go new file mode 100644 index 000000000..292bf0bf1 --- /dev/null +++ b/command/fix/fixer_virtualbox_rename.go @@ -0,0 +1,46 @@ +package fix + +import ( + "github.com/mitchellh/mapstructure" +) + +// FixerVirtualBoxRename changes "virtualbox" builders to "virtualbox-iso" +type FixerVirtualBoxRename struct{} + +func (FixerVirtualBoxRename) Fix(input map[string]interface{}) (map[string]interface{}, error) { + // The type we'll decode into; we only care about builders + type template struct { + Builders []map[string]interface{} + } + + // Decode the input into our structure, if we can + var tpl template + if err := mapstructure.Decode(input, &tpl); err != nil { + return nil, err + } + + for _, builder := range tpl.Builders { + builderTypeRaw, ok := builder["type"] + if !ok { + continue + } + + builderType, ok := builderTypeRaw.(string) + if !ok { + continue + } + + if builderType != "virtualbox" { + continue + } + + builder["type"] = "virtualbox-iso" + } + + input["builders"] = tpl.Builders + return input, nil +} + +func (FixerVirtualBoxRename) Synopsis() string { + return `Updates "virtualbox" builders to "virtualbox-iso"` +} diff --git a/command/fix/fixer_virtualbox_rename_test.go b/command/fix/fixer_virtualbox_rename_test.go new file mode 100644 index 000000000..1f529def1 --- /dev/null +++ b/command/fix/fixer_virtualbox_rename_test.go @@ -0,0 +1,49 @@ +package fix + +import ( + "reflect" + "testing" +) + +func TestFixerVirtualBoxRename_impl(t *testing.T) { + var _ Fixer = new(FixerVirtualBoxRename) +} + +func TestFixerVirtualBoxRename_Fix(t *testing.T) { + cases := []struct { + Input map[string]interface{} + Expected map[string]interface{} + }{ + // No attach field + { + Input: map[string]interface{}{ + "type": "virtualbox", + }, + + Expected: map[string]interface{}{ + "type": "virtualbox-iso", + }, + }, + } + + for _, tc := range cases { + var f FixerVirtualBoxRename + + input := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Input}, + } + + expected := map[string]interface{}{ + "builders": []map[string]interface{}{tc.Expected}, + } + + output, err := f.Fix(input) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(output, expected) { + t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected) + } + } +} diff --git a/command/fix/help.go b/command/fix/help.go index 88ab34070..95c5f3e16 100644 --- a/command/fix/help.go +++ b/command/fix/help.go @@ -17,5 +17,6 @@ Fixes that are run: to use "guest_additions_mode" pp-vagrant-override Replaces old-style provider overrides for the Vagrant post-processor to new-style as of Packer 0.5.0. + virtualbox-rename Updates "virtualbox" builders to "virtualbox-iso" ` From c2b3fa732ac3b03c1ce15e91fbc655cdcef1ea69 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 17:02:22 -0800 Subject: [PATCH 28/30] builder/virtualbox/ovf: validate source_path --- builder/virtualbox/ovf/config.go | 12 ++++++ builder/virtualbox/ovf/config_test.go | 54 ++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 853b81333..12141cc23 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -2,6 +2,8 @@ package ovf import ( "fmt" + "os" + vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common" "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" @@ -54,6 +56,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.VBoxVersionConfig.Prepare(c.tpl)...) templates := map[string]*string{ + "source_path": &c.SourcePath, "vm_name": &c.VMName, } @@ -66,6 +69,15 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } } + if c.SourcePath == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) + } else { + if _, err := os.Stat(c.SourcePath); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("source_path is invalid: %s", err)) + } + } + // Warnings var warnings []string if c.ShutdownCommand == "" { diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 0981344e0..56cb088f5 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -2,8 +2,60 @@ package ovf import ( "testing" + "io/ioutil" + "os" ) func testConfig(t *testing.T) map[string]interface{} { - return map[string]interface{}{} + return map[string]interface{}{ + "ssh_username": "foo", + "shutdown_command": "foo", + } } + +func testConfigErr(t *testing.T, warns []string, err error) { + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err == nil { + t.Fatal("should error") + } +} + +func testConfigOk(t *testing.T, warns []string, err error) { + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("bad: %s", err) + } +} + + +func TestNewConfig_sourcePath(t *testing.T) { + // Bad + c := testConfig(t) + delete(c, "source_path") + _, warns, errs := NewConfig(c) + testConfigErr(t, warns, errs) + + // Bad + c = testConfig(t) + c["source_path"] = "/i/dont/exist" + _, warns, errs = NewConfig(c) + testConfigErr(t, warns, errs) + + // Good + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + tf.Close() + defer os.Remove(tf.Name()) + + c = testConfig(t) + c["source_path"] = tf.Name() + _, warns, errs = NewConfig(c) + testConfigOk(t, warns, errs) +} + From 473fe8a950074724a3ed9084923fdce8dff15f9c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 17:04:26 -0800 Subject: [PATCH 29/30] builder/virtualbox/ovf: instantiate the driver --- builder/virtualbox/ovf/builder.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index fffd90530..5a80689aa 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -2,6 +2,7 @@ package ovf import ( "errors" + "fmt" "log" "github.com/mitchellh/multistep" @@ -31,9 +32,16 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Run executes a Packer build and returns a packer.Artifact representing // a VirtualBox appliance. func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + // Create the driver that we'll use to communicate with VirtualBox + driver, err := vboxcommon.NewDriver() + if err != nil { + return nil, fmt.Errorf("Failed creating VirtualBox driver: %s", err) + } + // Set up the state. state := new(multistep.BasicStateBag) state.Put("config", b.config) + state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) From c7bf38b63b50660ff6b1fe02324230b7b14c1067 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 17:08:07 -0800 Subject: [PATCH 30/30] builder/virtualbox/common: only remove ISO if it was attached --- .../virtualbox/common/step_remove_devices.go | 26 +++++++++-------- .../common/step_remove_devices_test.go | 28 ++++++++++++++++--- builder/virtualbox/iso/step_attach_iso.go | 3 ++ 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/builder/virtualbox/common/step_remove_devices.go b/builder/virtualbox/common/step_remove_devices.go index 176de50a4..2a11dcbba 100644 --- a/builder/virtualbox/common/step_remove_devices.go +++ b/builder/virtualbox/common/step_remove_devices.go @@ -40,19 +40,21 @@ func (s *StepRemoveDevices) Run(state multistep.StateBag) multistep.StepAction { } } - command := []string{ - "storageattach", vmName, - "--storagectl", "IDE Controller", - "--port", "0", - "--device", "1", - "--medium", "none", - } + if _, ok := state.GetOk("attachedIso"); ok { + command := []string{ + "storageattach", vmName, + "--storagectl", "IDE Controller", + "--port", "0", + "--device", "1", + "--medium", "none", + } - if err := driver.VBoxManage(command...); err != nil { - err := fmt.Errorf("Error detaching ISO: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt + if err := driver.VBoxManage(command...); err != nil { + err := fmt.Errorf("Error detaching ISO: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } } return multistep.ActionContinue diff --git a/builder/virtualbox/common/step_remove_devices_test.go b/builder/virtualbox/common/step_remove_devices_test.go index af1bccc1f..e74331232 100644 --- a/builder/virtualbox/common/step_remove_devices_test.go +++ b/builder/virtualbox/common/step_remove_devices_test.go @@ -25,6 +25,29 @@ func TestStepRemoveDevices(t *testing.T) { t.Fatal("should NOT have error") } + // Test that ISO was removed + if len(driver.VBoxManageCalls) != 0 { + t.Fatalf("bad: %#v", driver.VBoxManageCalls) + } +} + +func TestStepRemoveDevices_attachedIso(t *testing.T) { + state := testState(t) + step := new(StepRemoveDevices) + + state.Put("attachedIso", true) + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + if action := step.Run(state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + if _, ok := state.GetOk("error"); ok { + t.Fatal("should NOT have error") + } + // Test that ISO was removed if len(driver.VBoxManageCalls) != 1 { t.Fatalf("bad: %#v", driver.VBoxManageCalls) @@ -52,13 +75,10 @@ func TestStepRemoveDevices_floppyPath(t *testing.T) { } // Test that both were removed - if len(driver.VBoxManageCalls) != 2 { + if len(driver.VBoxManageCalls) != 1 { t.Fatalf("bad: %#v", driver.VBoxManageCalls) } if driver.VBoxManageCalls[0][3] != "Floppy Controller" { t.Fatalf("bad: %#v", driver.VBoxManageCalls) } - if driver.VBoxManageCalls[1][3] != "IDE Controller" { - t.Fatalf("bad: %#v", driver.VBoxManageCalls) - } } diff --git a/builder/virtualbox/iso/step_attach_iso.go b/builder/virtualbox/iso/step_attach_iso.go index 5d4ec42ef..f18281434 100644 --- a/builder/virtualbox/iso/step_attach_iso.go +++ b/builder/virtualbox/iso/step_attach_iso.go @@ -41,6 +41,9 @@ func (s *stepAttachISO) Run(state multistep.StateBag) multistep.StepAction { // Track the path so that we can unregister it from VirtualBox later s.diskPath = isoPath + // Set some state so we know to remove + state.Put("attachedIso", true) + return multistep.ActionContinue }