From e93697ab4e4afd86988c10e5bf741fdf060c3b0b Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Mon, 12 May 2014 19:02:30 -0700 Subject: [PATCH 1/2] builder/virtualbox-ovf,vmware-vmx: add `boot_command` support Fixes #1082 --- .../{iso => common}/step_type_boot_command.go | 23 ++++++++++--------- builder/virtualbox/iso/builder.go | 6 ++++- builder/virtualbox/ovf/builder.go | 5 ++++ builder/virtualbox/ovf/config.go | 22 ++++++++++++------ builder/vmware/{iso => common}/host_ip.go | 2 +- .../{iso => common}/host_ip_ifconfig.go | 2 +- .../{iso => common}/host_ip_ifconfig_test.go | 2 +- .../{iso => common}/host_ip_vmnetnatconf.go | 6 ++--- .../host_ip_vmnetnatconf_test.go | 2 +- .../{iso => common}/step_type_boot_command.go | 23 ++++++++++--------- builder/vmware/iso/builder.go | 6 ++++- builder/vmware/vmx/builder.go | 5 ++++ builder/vmware/vmx/config.go | 8 +++++++ 13 files changed, 73 insertions(+), 39 deletions(-) rename builder/virtualbox/{iso => common}/step_type_boot_command.go (92%) rename builder/vmware/{iso => common}/host_ip.go (91%) rename builder/vmware/{iso => common}/host_ip_ifconfig.go (98%) rename builder/vmware/{iso => common}/host_ip_ifconfig_test.go (93%) rename builder/vmware/{iso => common}/host_ip_vmnetnatconf.go (90%) rename builder/vmware/{iso => common}/host_ip_vmnetnatconf_test.go (93%) rename builder/vmware/{iso => common}/step_type_boot_command.go (91%) 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 f41118a45..32a3f7ff5 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -303,7 +303,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 312fc4701..5b61ba6fa 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -84,6 +84,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 a8df48791..b11370958 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -24,13 +24,14 @@ type Config struct { vboxcommon.VBoxManagePostConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"` - SourcePath string `mapstructure:"source_path"` - GuestAdditionsMode string `mapstructure:"guest_additions_mode"` - GuestAdditionsPath string `mapstructure:"guest_additions_path"` - GuestAdditionsURL string `mapstructure:"guest_additions_url"` - GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` - VMName string `mapstructure:"vm_name"` - ImportOpts string `mapstructure:"import_opts"` + BootCommand []string `mapstructure:"boot_command"` + SourcePath string `mapstructure:"source_path"` + GuestAdditionsMode string `mapstructure:"guest_additions_mode"` + GuestAdditionsPath string `mapstructure:"guest_additions_path"` + GuestAdditionsURL string `mapstructure:"guest_additions_url"` + GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` + VMName string `mapstructure:"vm_name"` + ImportOpts string `mapstructure:"import_opts"` tpl *packer.ConfigTemplate } @@ -99,6 +100,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 d1f24fa64..8acfe65c8 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 e75f62b57..5f14f2505 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 4aac4d970..4c3db70f6 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"` @@ -72,6 +73,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 { From e082a7e59b05003e6af9da273cba79c902ac2a0a Mon Sep 17 00:00:00 2001 From: Ross Smith II Date: Mon, 12 May 2014 21:38:40 -0700 Subject: [PATCH 2/2] added docs for boot_command and boot_wait --- .../docs/builders/virtualbox-ovf.html.markdown | 13 +++++++++++++ .../source/docs/builders/vmware-vmx.html.markdown | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/website/source/docs/builders/virtualbox-ovf.html.markdown b/website/source/docs/builders/virtualbox-ovf.html.markdown index 4a41021b2..238d72c39 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 2632cacfa..84ee7af64 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