Commit e93697ab authored by Ross Smith II's avatar Ross Smith II

builder/virtualbox-ovf,vmware-vmx: add `boot_command` support

Fixes #1082
parent 38d1d7fd
package iso package common
import ( import (
"fmt" "fmt"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
vboxcommon "github.com/mitchellh/packer/builder/virtualbox/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"strings" "strings"
...@@ -23,7 +22,6 @@ type bootCommandTemplateData struct { ...@@ -23,7 +22,6 @@ type bootCommandTemplateData struct {
// This step "types" the boot command into the VM over VNC. // This step "types" the boot command into the VM over VNC.
// //
// Uses: // Uses:
// config *config
// driver Driver // driver Driver
// http_port int // http_port int
// ui packer.Ui // ui packer.Ui
...@@ -31,11 +29,14 @@ type bootCommandTemplateData struct { ...@@ -31,11 +29,14 @@ type bootCommandTemplateData struct {
// //
// Produces: // Produces:
// <nothing> // <nothing>
type stepTypeBootCommand struct{} type StepTypeBootCommand struct {
BootCommand []string
VMName string
Tpl *packer.ConfigTemplate
}
func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config) driver := state.Get("driver").(Driver)
driver := state.Get("driver").(vboxcommon.Driver)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(uint)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)
...@@ -43,12 +44,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction ...@@ -43,12 +44,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
tplData := &bootCommandTemplateData{ tplData := &bootCommandTemplateData{
"10.0.2.2", "10.0.2.2",
httpPort, httpPort,
config.VMName, s.VMName,
} }
ui.Say("Typing the boot command...") ui.Say("Typing the boot command...")
for _, command := range config.BootCommand { for _, command := range s.BootCommand {
command, err := config.tpl.Process(command, tplData) command, err := s.Tpl.Process(command, tplData)
if err != nil { if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err) err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err) state.Put("error", err)
...@@ -90,7 +91,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction ...@@ -90,7 +91,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
return multistep.ActionContinue return multistep.ActionContinue
} }
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
func scancodes(message string) []string { func scancodes(message string) []string {
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
......
...@@ -303,7 +303,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -303,7 +303,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
BootWait: b.config.BootWait, BootWait: b.config.BootWait,
Headless: b.config.Headless, Headless: b.config.Headless,
}, },
new(stepTypeBootCommand), &vboxcommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
&common.StepConnectSSH{ &common.StepConnectSSH{
SSHAddress: vboxcommon.SSHAddress, SSHAddress: vboxcommon.SSHAddress,
SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig),
......
...@@ -84,6 +84,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -84,6 +84,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
BootWait: b.config.BootWait, BootWait: b.config.BootWait,
Headless: b.config.Headless, Headless: b.config.Headless,
}, },
&vboxcommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
&common.StepConnectSSH{ &common.StepConnectSSH{
SSHAddress: vboxcommon.SSHAddress, SSHAddress: vboxcommon.SSHAddress,
SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig), SSHConfig: vboxcommon.SSHConfigFunc(b.config.SSHConfig),
......
...@@ -24,13 +24,14 @@ type Config struct { ...@@ -24,13 +24,14 @@ type Config struct {
vboxcommon.VBoxManagePostConfig `mapstructure:",squash"` vboxcommon.VBoxManagePostConfig `mapstructure:",squash"`
vboxcommon.VBoxVersionConfig `mapstructure:",squash"` vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
SourcePath string `mapstructure:"source_path"` BootCommand []string `mapstructure:"boot_command"`
GuestAdditionsMode string `mapstructure:"guest_additions_mode"` SourcePath string `mapstructure:"source_path"`
GuestAdditionsPath string `mapstructure:"guest_additions_path"` GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
GuestAdditionsURL string `mapstructure:"guest_additions_url"` GuestAdditionsPath string `mapstructure:"guest_additions_path"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"` GuestAdditionsURL string `mapstructure:"guest_additions_url"`
VMName string `mapstructure:"vm_name"` GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
ImportOpts string `mapstructure:"import_opts"` VMName string `mapstructure:"vm_name"`
ImportOpts string `mapstructure:"import_opts"`
tpl *packer.ConfigTemplate tpl *packer.ConfigTemplate
} }
...@@ -99,6 +100,13 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { ...@@ -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{ validates := map[string]*string{
"guest_additions_path": &c.GuestAdditionsPath, "guest_additions_path": &c.GuestAdditionsPath,
"guest_additions_url": &c.GuestAdditionsURL, "guest_additions_url": &c.GuestAdditionsURL,
......
package iso package common
// Interface to help find the host IP that is available from within // Interface to help find the host IP that is available from within
// the VMware virtual machines. // the VMware virtual machines.
......
package iso package common
import ( import (
"bufio" "bufio"
...@@ -8,8 +8,6 @@ import ( ...@@ -8,8 +8,6 @@ import (
"os" "os"
"regexp" "regexp"
"strings" "strings"
vmwcommon "github.com/mitchellh/packer/builder/vmware/common"
) )
// VMnetNatConfIPFinder finds the IP address of the host machine by // VMnetNatConfIPFinder finds the IP address of the host machine by
...@@ -18,7 +16,7 @@ import ( ...@@ -18,7 +16,7 @@ import (
type VMnetNatConfIPFinder struct{} type VMnetNatConfIPFinder struct{}
func (*VMnetNatConfIPFinder) HostIP() (string, error) { func (*VMnetNatConfIPFinder) HostIP() (string, error) {
driver := &vmwcommon.Workstation9Driver{} driver := &Workstation9Driver{}
vmnetnat := driver.VmnetnatConfPath() vmnetnat := driver.VmnetnatConfPath()
if vmnetnat == "" { if vmnetnat == "" {
......
package iso package common
import ( import (
"fmt" "fmt"
"github.com/mitchellh/go-vnc" "github.com/mitchellh/go-vnc"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
vmwcommon "github.com/mitchellh/packer/builder/vmware/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"net" "net"
...@@ -26,18 +25,20 @@ type bootCommandTemplateData struct { ...@@ -26,18 +25,20 @@ type bootCommandTemplateData struct {
// This step "types" the boot command into the VM over VNC. // This step "types" the boot command into the VM over VNC.
// //
// Uses: // Uses:
// config *config
// http_port int // http_port int
// ui packer.Ui // ui packer.Ui
// vnc_port uint // vnc_port uint
// //
// Produces: // Produces:
// <nothing> // <nothing>
type stepTypeBootCommand struct{} type StepTypeBootCommand struct {
BootCommand []string
VMName string
Tpl *packer.ConfigTemplate
}
func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { func (s *StepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config) driver := state.Get("driver").(Driver)
driver := state.Get("driver").(vmwcommon.Driver)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(uint)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vncIp := state.Get("vnc_ip").(string) vncIp := state.Get("vnc_ip").(string)
...@@ -88,12 +89,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction ...@@ -88,12 +89,12 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
tplData := &bootCommandTemplateData{ tplData := &bootCommandTemplateData{
hostIp, hostIp,
httpPort, httpPort,
config.VMName, s.VMName,
} }
ui.Say("Typing the boot command over VNC...") ui.Say("Typing the boot command over VNC...")
for _, command := range config.BootCommand { for _, command := range s.BootCommand {
command, err := config.tpl.Process(command, tplData) command, err := s.Tpl.Process(command, tplData)
if err != nil { if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err) err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err) state.Put("error", err)
...@@ -113,7 +114,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction ...@@ -113,7 +114,7 @@ func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction
return multistep.ActionContinue return multistep.ActionContinue
} }
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) { func vncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h // Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
......
...@@ -348,7 +348,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -348,7 +348,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
DurationBeforeStop: 5 * time.Second, DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless, Headless: b.config.Headless,
}, },
&stepTypeBootCommand{}, &vmwcommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
&common.StepConnectSSH{ &common.StepConnectSSH{
SSHAddress: driver.SSHAddress, SSHAddress: driver.SSHAddress,
SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig),
......
...@@ -76,6 +76,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ...@@ -76,6 +76,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
DurationBeforeStop: 5 * time.Second, DurationBeforeStop: 5 * time.Second,
Headless: b.config.Headless, Headless: b.config.Headless,
}, },
&vmwcommon.StepTypeBootCommand{
BootCommand: b.config.BootCommand,
VMName: b.config.VMName,
Tpl: b.config.tpl,
},
&common.StepConnectSSH{ &common.StepConnectSSH{
SSHAddress: driver.SSHAddress, SSHAddress: driver.SSHAddress,
SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig), SSHConfig: vmwcommon.SSHConfigFunc(&b.config.SSHConfig),
......
...@@ -20,6 +20,7 @@ type Config struct { ...@@ -20,6 +20,7 @@ type Config struct {
vmwcommon.ToolsConfig `mapstructure:",squash"` vmwcommon.ToolsConfig `mapstructure:",squash"`
vmwcommon.VMXConfig `mapstructure:",squash"` vmwcommon.VMXConfig `mapstructure:",squash"`
BootCommand []string `mapstructure:"boot_command"`
FloppyFiles []string `mapstructure:"floppy_files"` FloppyFiles []string `mapstructure:"floppy_files"`
RemoteType string `mapstructure:"remote_type"` RemoteType string `mapstructure:"remote_type"`
SkipCompaction bool `mapstructure:"skip_compaction"` SkipCompaction bool `mapstructure:"skip_compaction"`
...@@ -72,6 +73,13 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { ...@@ -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 == "" { if c.SourcePath == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required")) errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is required"))
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment