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

Fixes #1082
This commit is contained in:
Ross Smith II 2014-05-12 19:02:30 -07:00
parent 38d1d7fd3c
commit e93697ab4e
13 changed files with 73 additions and 39 deletions

View File

@ -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:
// <nothing>
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

View File

@ -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),

View File

@ -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),

View File

@ -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,

View File

@ -1,4 +1,4 @@
package iso
package common
// Interface to help find the host IP that is available from within
// the VMware virtual machines.

View File

@ -1,4 +1,4 @@
package iso
package common
import (
"bytes"

View File

@ -1,4 +1,4 @@
package iso
package common
import "testing"

View File

@ -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 == "" {

View File

@ -1,4 +1,4 @@
package iso
package common
import "testing"

View File

@ -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:
// <nothing>
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

View File

@ -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),

View File

@ -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),

View File

@ -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 {