From dd767c9d546a6c8123a0d828594e344488001dc4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 22 Dec 2013 15:19:19 -0800 Subject: [PATCH] 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 +}