diff --git a/builder/virtualbox/iso/step_type_boot_command.go b/builder/virtualbox/common/step_type_boot_command.go similarity index 92% rename from builder/virtualbox/iso/step_type_boot_command.go rename to builder/virtualbox/common/step_type_boot_command.go index 3e087f0df..d22584951 100644 --- a/builder/virtualbox/iso/step_type_boot_command.go +++ b/builder/virtualbox/common/step_type_boot_command.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" "strings" @@ -23,7 +22,6 @@ type bootCommandTemplateData struct { // This step "types" the boot command into the VM over VNC. // // Uses: -// config *config // driver Driver // http_port int // ui packer.Ui @@ -31,11 +29,14 @@ type bootCommandTemplateData struct { // // Produces: // -type stepTypeBootCommand struct{} +type StepTypeBootCommand struct { + BootCommand []string + VMName string + Tpl *packer.ConfigTemplate +} -func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vboxcommon.Driver) +func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) httpPort := state.Get("http_port").(uint) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) @@ -43,12 +44,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction tplData := &bootCommandTemplateData{ "10.0.2.2", httpPort, - config.VMName, + s.VMName, } ui.Say("Typing the boot command...") - for _, command := range config.BootCommand { - command, err := config.tpl.Process(command, tplData) + for _, command := range s.BootCommand { + command, err := s.Tpl.Process(command, tplData) if err != nil { err := fmt.Errorf("Error preparing boot command: %s", err) state.Put("error", err) @@ -90,7 +91,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } -func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} +func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {} func scancodes(message string) []string { // Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index f0acf83be..cbfac90cb 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -320,7 +320,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe BootWait: b.config.BootWait, Headless: b.config.Headless, }, - new(stepTypeBootCommand), + &vboxcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + VMName: b.config.VMName, + Tpl: b.config.tpl, + }, &common.StepConnectSSH{ SSHAddress: vboxcommon.SSHAddress, SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index c41f9a1a4..eeacdf1e8 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -89,6 +89,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe BootWait: b.config.BootWait, Headless: b.config.Headless, }, + &vboxcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + VMName: b.config.VMName, + Tpl: b.config.tpl, + }, &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 4d93a59a0..885687b67 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -24,6 +24,7 @@ type Config struct { vboxcommon.VBoxManagePostConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"` + BootCommand []string `mapstructure:"boot_command"` SourcePath string `mapstructure:"source_path"` GuestAdditionsMode string `mapstructure:"guest_additions_mode"` GuestAdditionsPath string `mapstructure:"guest_additions_path"` @@ -115,6 +116,13 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } } + for i, command := range c.BootCommand { + if err := c.tpl.Validate(command); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Error processing boot_command[%d]: %s", i, err)) + } + } + validates := map[string]*string{ "guest_additions_path": &c.GuestAdditionsPath, "guest_additions_url": &c.GuestAdditionsURL, diff --git a/builder/vmware/iso/host_ip.go b/builder/vmware/common/host_ip.go similarity index 91% rename from builder/vmware/iso/host_ip.go rename to builder/vmware/common/host_ip.go index 98a6d26df..f920043e7 100644 --- a/builder/vmware/iso/host_ip.go +++ b/builder/vmware/common/host_ip.go @@ -1,4 +1,4 @@ -package iso +package common // Interface to help find the host IP that is available from within // the VMware virtual machines. diff --git a/builder/vmware/iso/host_ip_ifconfig.go b/builder/vmware/common/host_ip_ifconfig.go similarity index 98% rename from builder/vmware/iso/host_ip_ifconfig.go rename to builder/vmware/common/host_ip_ifconfig.go index 300107007..2a985aada 100644 --- a/builder/vmware/iso/host_ip_ifconfig.go +++ b/builder/vmware/common/host_ip_ifconfig.go @@ -1,4 +1,4 @@ -package iso +package common import ( "bytes" diff --git a/builder/vmware/iso/host_ip_ifconfig_test.go b/builder/vmware/common/host_ip_ifconfig_test.go similarity index 93% rename from builder/vmware/iso/host_ip_ifconfig_test.go rename to builder/vmware/common/host_ip_ifconfig_test.go index 51ccc272c..a69104e69 100644 --- a/builder/vmware/iso/host_ip_ifconfig_test.go +++ b/builder/vmware/common/host_ip_ifconfig_test.go @@ -1,4 +1,4 @@ -package iso +package common import "testing" diff --git a/builder/vmware/iso/host_ip_vmnetnatconf.go b/builder/vmware/common/host_ip_vmnetnatconf.go similarity index 90% rename from builder/vmware/iso/host_ip_vmnetnatconf.go rename to builder/vmware/common/host_ip_vmnetnatconf.go index 0ba0a3727..e07227a5e 100644 --- a/builder/vmware/iso/host_ip_vmnetnatconf.go +++ b/builder/vmware/common/host_ip_vmnetnatconf.go @@ -1,4 +1,4 @@ -package iso +package common import ( "bufio" @@ -8,8 +8,6 @@ import ( "os" "regexp" "strings" - - vmwcommon "github.com/mitchellh/packer/builder/vmware/common" ) // VMnetNatConfIPFinder finds the IP address of the host machine by @@ -18,7 +16,7 @@ import ( type VMnetNatConfIPFinder struct{} func (*VMnetNatConfIPFinder) HostIP() (string, error) { - driver := &vmwcommon.Workstation9Driver{} + driver := &Workstation9Driver{} vmnetnat := driver.VmnetnatConfPath() if vmnetnat == "" { diff --git a/builder/vmware/iso/host_ip_vmnetnatconf_test.go b/builder/vmware/common/host_ip_vmnetnatconf_test.go similarity index 93% rename from builder/vmware/iso/host_ip_vmnetnatconf_test.go rename to builder/vmware/common/host_ip_vmnetnatconf_test.go index 873a7fa2b..9f3114a92 100644 --- a/builder/vmware/iso/host_ip_vmnetnatconf_test.go +++ b/builder/vmware/common/host_ip_vmnetnatconf_test.go @@ -1,4 +1,4 @@ -package iso +package common import "testing" diff --git a/builder/vmware/iso/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go similarity index 91% rename from builder/vmware/iso/step_type_boot_command.go rename to builder/vmware/common/step_type_boot_command.go index 4dbc7f82d..ac1da0131 100644 --- a/builder/vmware/iso/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -1,10 +1,9 @@ -package iso +package common import ( "fmt" "github.com/mitchellh/go-vnc" "github.com/mitchellh/multistep" - vmwcommon "github.com/mitchellh/packer/builder/vmware/common" "github.com/mitchellh/packer/packer" "log" "net" @@ -26,18 +25,20 @@ type bootCommandTemplateData struct { // This step "types" the boot command into the VM over VNC. // // Uses: -// config *config // http_port int // ui packer.Ui // vnc_port uint // // Produces: // -type stepTypeBootCommand struct{} +type StepTypeBootCommand struct { + BootCommand []string + VMName string + Tpl *packer.ConfigTemplate +} -func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*config) - driver := state.Get("driver").(vmwcommon.Driver) +func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { + driver := state.Get("driver").(Driver) httpPort := state.Get("http_port").(uint) ui := state.Get("ui").(packer.Ui) vncIp := state.Get("vnc_ip").(string) @@ -88,12 +89,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction tplData := &bootCommandTemplateData{ hostIp, httpPort, - config.VMName, + s.VMName, } ui.Say("Typing the boot command over VNC...") - for _, command := range config.BootCommand { - command, err := config.tpl.Process(command, tplData) + for _, command := range s.BootCommand { + command, err := s.Tpl.Process(command, tplData) if err != nil { err := fmt.Errorf("Error preparing boot command: %s", err) state.Put("error", err) @@ -113,7 +114,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } -func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} +func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {} func vncSendString(c *vnc.ClientConn, original string) { // Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 7c90b298d..574b2fb24 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -348,7 +348,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe DurationBeforeStop: 5 * time.Second, Headless: b.config.Headless, }, - &stepTypeBootCommand{}, + &vmwcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + VMName: b.config.VMName, + Tpl: b.config.tpl, + }, &common.StepConnectSSH{ SSHAddress: driver.SSHAddress, SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 61c5f9da6..1d44f4485 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -76,6 +76,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe DurationBeforeStop: 5 * time.Second, Headless: b.config.Headless, }, + &vmwcommon.StepTypeBootCommand{ + BootCommand: b.config.BootCommand, + VMName: b.config.VMName, + Tpl: b.config.tpl, + }, &common.StepConnectSSH{ SSHAddress: driver.SSHAddress, SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 75fb6440f..575bc7a5b 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -20,6 +20,7 @@ type Config struct { vmwcommon.ToolsConfig `mapstructure:",squash"` vmwcommon.VMXConfig `mapstructure:",squash"` + BootCommand []string `mapstructure:"boot_command"` FloppyFiles []string `mapstructure:"floppy_files"` RemoteType string `mapstructure:"remote_type"` SkipCompaction bool `mapstructure:"skip_compaction"` @@ -82,6 +83,13 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } } + for i, command := range c.BootCommand { + if err := c.tpl.Validate(command); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Error processing boot_command[%d]: %s", i, err)) + } + } + if c.SourcePath == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) } else { diff --git a/website/source/docs/builders/virtualbox-ovf.html.markdown b/website/source/docs/builders/virtualbox-ovf.html.markdown index b9fbf7064..90acd7489 100644 --- a/website/source/docs/builders/virtualbox-ovf.html.markdown +++ b/website/source/docs/builders/virtualbox-ovf.html.markdown @@ -52,6 +52,19 @@ each category, the available options are alphabetized and described. ### 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. + * `export_opts` (array of strings) - Additional options to pass to the `VBoxManage export`. This can be useful for passing product information to include in the resulting appliance file. diff --git a/website/source/docs/builders/vmware-vmx.html.markdown b/website/source/docs/builders/vmware-vmx.html.markdown index b733473e7..d32978969 100644 --- a/website/source/docs/builders/vmware-vmx.html.markdown +++ b/website/source/docs/builders/vmware-vmx.html.markdown @@ -51,6 +51,19 @@ each category, the available options are alphabetized and described. ### Optional: +* `boot_command` (array of strings) - This is an array of commands to type + when the virtual machine is firsted 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. + * `floppy_files` (array of strings) - A list of files to place onto a floppy disk that is attached when the VM is booted. This is most useful for unattended Windows installs, which look for an `Autounattend.xml` file