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() + } +}