diff --git a/builder/virtualbox/builder.go b/builder/virtualbox/builder.go index ba53192c9..de6a0ce68 100644 --- a/builder/virtualbox/builder.go +++ b/builder/virtualbox/builder.go @@ -43,6 +43,7 @@ type config struct { SSHPort uint `mapstructure:"ssh_port"` SSHUser string `mapstructure:"ssh_username"` SSHWaitTimeout time.Duration `` + VBoxVersionFile string `mapstructure:"virtualbox_version_file"` VBoxManage [][]string `mapstructure:"vboxmanage"` VMName string `mapstructure:"vm_name"` @@ -99,6 +100,10 @@ func (b *Builder) Prepare(raws ...interface{}) error { b.config.VBoxManage = make([][]string, 0) } + if b.config.VBoxVersionFile == "" { + b.config.VBoxVersionFile = ".vbox_version" + } + if b.config.VMName == "" { b.config.VMName = "packer" } @@ -213,6 +218,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe new(stepRun), new(stepTypeBootCommand), new(stepWaitForSSH), + new(stepUploadVersion), new(stepProvision), new(stepShutdown), new(stepExport), diff --git a/builder/virtualbox/builder_test.go b/builder/virtualbox/builder_test.go index e32eb1064..4b6633aed 100644 --- a/builder/virtualbox/builder_test.go +++ b/builder/virtualbox/builder_test.go @@ -315,3 +315,31 @@ func TestBuilderPrepare_VBoxManage(t *testing.T) { t.Fatalf("bad: %#v", b.config.VBoxManage) } } + +func TestBuilderPrepare_VBoxVersionFile(t *testing.T) { + var b Builder + config := testConfig() + + // Test empty + delete(config, "virtualbox_version_file") + err := b.Prepare(config) + 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{} + err = b.Prepare(config) + 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/driver.go b/builder/virtualbox/driver.go index a0b6ee343..093338eff 100644 --- a/builder/virtualbox/driver.go +++ b/builder/virtualbox/driver.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "os/exec" + "regexp" "strings" "time" ) @@ -29,6 +30,9 @@ type Driver interface { // 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 { @@ -104,3 +108,21 @@ func (d *VBox42Driver) VBoxManage(args ...string) error { func (d *VBox42Driver) Verify() error { return nil } + +func (d *VBox42Driver) Version() (string, error) { + var stdout bytes.Buffer + + cmd := exec.Command(d.VBoxManagePath, "--version") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + versionRe := regexp.MustCompile("[^.0-9]") + matches := versionRe.Split(stdout.String(), 2) + if len(matches) == 0 { + return "", fmt.Errorf("No version found: %s", stdout.String()) + } + + return matches[0], nil +} diff --git a/builder/virtualbox/step_upload_version.go b/builder/virtualbox/step_upload_version.go new file mode 100644 index 000000000..5f825aaf9 --- /dev/null +++ b/builder/virtualbox/step_upload_version.go @@ -0,0 +1,37 @@ +package virtualbox + +import ( + "bytes" + "fmt" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +// This step uploads a file containing the VirtualBox version, which +// can be useful for various provisioning reasons. +type stepUploadVersion struct{} + +func (s *stepUploadVersion) Run(state map[string]interface{}) multistep.StepAction { + comm := state["communicator"].(packer.Communicator) + config := state["config"].(*config) + driver := state["driver"].(Driver) + ui := state["ui"].(packer.Ui) + + version, err := driver.Version() + if err != nil { + state["error"] = fmt.Errorf("Error reading version for metadata upload: %s", err) + return multistep.ActionHalt + } + + 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 { + state["error"] = fmt.Errorf("Error uploading VirtualBox version: %s", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *stepUploadVersion) Cleanup(state map[string]interface{}) {}