diff --git a/builder/hyperv/common/step_type_boot_command.go b/builder/hyperv/common/step_type_boot_command.go index de96d4293..dc4b27c85 100644 --- a/builder/hyperv/common/step_type_boot_command.go +++ b/builder/hyperv/common/step_type_boot_command.go @@ -21,10 +21,11 @@ type bootCommandTemplateData struct { // This step "types" the boot command into the VM via the Hyper-V virtual keyboard type StepTypeBootCommand struct { - BootCommand string - BootWait time.Duration - SwitchName string - Ctx interpolate.Context + BootCommand string + BootWait time.Duration + SwitchName string + Ctx interpolate.Context + GroupInterval time.Duration } func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -66,7 +67,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) scanCodesToSendString := strings.Join(codes, " ") return driver.TypeScanCodes(vmName, scanCodesToSendString) } - d := bootcommand.NewPCXTDriver(sendCodes, -1) + d := bootcommand.NewPCXTDriver(sendCodes, -1, s.GroupInterval) ui.Say("Typing the boot command...") command, err := interpolate.Render(s.BootCommand, &s.Ctx) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index d50e5e26a..15887eeeb 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -434,10 +434,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &hypervcommon.StepTypeBootCommand{ - BootCommand: b.config.FlatBootCommand(), - BootWait: b.config.BootWait, - SwitchName: b.config.SwitchName, - Ctx: b.config.ctx, + BootCommand: b.config.FlatBootCommand(), + BootWait: b.config.BootWait, + SwitchName: b.config.SwitchName, + Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, // configure the communicator ssh, winrm diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 987c41a12..1f53df9e6 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -443,10 +443,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &hypervcommon.StepTypeBootCommand{ - BootCommand: b.config.FlatBootCommand(), - BootWait: b.config.BootWait, - SwitchName: b.config.SwitchName, - Ctx: b.config.ctx, + BootCommand: b.config.FlatBootCommand(), + BootWait: b.config.BootWait, + SwitchName: b.config.SwitchName, + Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, // configure the communicator ssh, winrm diff --git a/builder/parallels/common/step_type_boot_command.go b/builder/parallels/common/step_type_boot_command.go index ed21c049d..4e1e47b20 100644 --- a/builder/parallels/common/step_type_boot_command.go +++ b/builder/parallels/common/step_type_boot_command.go @@ -26,6 +26,7 @@ type StepTypeBootCommand struct { HostInterfaces []string VMName string Ctx interpolate.Context + GroupInterval time.Duration } // Run types the boot command by sending key scancodes into the VM. @@ -79,7 +80,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) sendCodes := func(codes []string) error { return driver.SendKeyScanCodes(s.VMName, codes...) } - d := bootcommand.NewPCXTDriver(sendCodes, -1) + d := bootcommand.NewPCXTDriver(sendCodes, -1, s.GroupInterval) ui.Say("Typing the boot command...") command, err := interpolate.Render(s.BootCommand, &s.Ctx) diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index 9ba2002f7..9cf7f0405 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -192,6 +192,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe HostInterfaces: b.config.HostInterfaces, VMName: b.config.VMName, Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/builder/parallels/pvm/builder.go b/builder/parallels/pvm/builder.go index e36916092..331b71670 100644 --- a/builder/parallels/pvm/builder.go +++ b/builder/parallels/pvm/builder.go @@ -81,6 +81,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe HostInterfaces: []string{}, VMName: b.config.VMName, Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/builder/qemu/step_type_boot_command.go b/builder/qemu/step_type_boot_command.go index 79bcd1338..11ebf3621 100644 --- a/builder/qemu/step_type_boot_command.go +++ b/builder/qemu/step_type_boot_command.go @@ -95,7 +95,7 @@ func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) config.VMName, } - d := bootcommand.NewVNCDriver(c) + d := bootcommand.NewVNCDriver(c, config.VNCConfig.BootKeyInterval) ui.Say("Typing the boot command over VNC...") command, err := interpolate.Render(config.VNCConfig.FlatBootCommand(), &configCtx) diff --git a/builder/virtualbox/common/step_type_boot_command.go b/builder/virtualbox/common/step_type_boot_command.go index 0effc3628..859e425ff 100644 --- a/builder/virtualbox/common/step_type_boot_command.go +++ b/builder/virtualbox/common/step_type_boot_command.go @@ -21,10 +21,11 @@ type bootCommandTemplateData struct { } type StepTypeBootCommand struct { - BootCommand string - BootWait time.Duration - VMName string - Ctx interpolate.Context + BootCommand string + BootWait time.Duration + VMName string + Ctx interpolate.Context + GroupInterval time.Duration } func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -64,7 +65,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) return driver.VBoxManage(args...) } - d := bootcommand.NewPCXTDriver(sendCodes, 25) + d := bootcommand.NewPCXTDriver(sendCodes, 25, s.GroupInterval) ui.Say("Typing the boot command...") command, err := interpolate.Render(s.BootCommand, &s.Ctx) diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 2ea19e70f..5fe773b52 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -245,10 +245,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Headless: b.config.Headless, }, &vboxcommon.StepTypeBootCommand{ - BootWait: b.config.BootWait, - BootCommand: b.config.FlatBootCommand(), - VMName: b.config.VMName, - Ctx: b.config.ctx, + BootWait: b.config.BootWait, + BootCommand: b.config.FlatBootCommand(), + VMName: b.config.VMName, + Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index 09fd416a3..54c8dc013 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -106,10 +106,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Headless: b.config.Headless, }, &vboxcommon.StepTypeBootCommand{ - BootWait: b.config.BootWait, - BootCommand: b.config.FlatBootCommand(), - VMName: b.config.VMName, - Ctx: b.config.ctx, + BootWait: b.config.BootWait, + BootCommand: b.config.FlatBootCommand(), + VMName: b.config.VMName, + Ctx: b.config.ctx, + GroupInterval: b.config.BootConfig.BootGroupInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index 83d55ce11..ac33fd24c 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -30,6 +30,7 @@ type StepTypeBootCommand struct { BootWait time.Duration VMName string Ctx interpolate.Context + KeyInterval time.Duration } type bootCommandTemplateData struct { HTTPIP string @@ -115,7 +116,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) s.VMName, } - d := bootcommand.NewVNCDriver(c) + d := bootcommand.NewVNCDriver(c, s.KeyInterval) ui.Say("Typing the boot command over VNC...") command, err := interpolate.Render(s.BootCommand, &s.Ctx) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 0944db927..3b2317d61 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -339,6 +339,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe BootCommand: b.config.FlatBootCommand(), VMName: b.config.VMName, Ctx: b.config.ctx, + KeyInterval: b.config.VNCConfig.BootKeyInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index dbb74b7a1..d6d748c6b 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -97,6 +97,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe BootCommand: b.config.FlatBootCommand(), VMName: b.config.VMName, Ctx: b.config.ctx, + KeyInterval: b.config.VNCConfig.BootKeyInterval, }, &communicator.StepConnect{ Config: &b.config.SSHConfig.Comm, diff --git a/common/bootcommand/config.go b/common/bootcommand/config.go index 532c7c82c..d01e69624 100644 --- a/common/bootcommand/config.go +++ b/common/bootcommand/config.go @@ -9,21 +9,26 @@ import ( ) type BootConfig struct { - RawBootWait string `mapstructure:"boot_wait"` - BootCommand []string `mapstructure:"boot_command"` - - BootWait time.Duration `` + RawBootGroupInterval string `mapstructure:"boot_keygroup_interval"` + RawBootWait string `mapstructure:"boot_wait"` + BootCommand []string `mapstructure:"boot_command"` + BootGroupInterval time.Duration `` + BootWait time.Duration `` } type VNCConfig struct { BootConfig `mapstructure:",squash"` DisableVNC bool `mapstructure:"disable_vnc"` + // time in ms to wait between each key press + RawBootKeyInterval string `mapstructure:"boot_key_interval"` + BootKeyInterval time.Duration `` } func (c *BootConfig) Prepare(ctx *interpolate.Context) (errs []error) { if c.RawBootWait == "" { c.RawBootWait = "10s" } + if c.RawBootWait != "" { bw, err := time.ParseDuration(c.RawBootWait) if err != nil { @@ -34,6 +39,20 @@ func (c *BootConfig) Prepare(ctx *interpolate.Context) (errs []error) { } } + if c.RawBootGroupInterval == "" { + c.RawBootGroupInterval = "0ms" + } + + if c.RawBootGroupInterval != "" { + bgi, err := time.ParseDuration(c.RawBootGroupInterval) + if err != nil { + errs = append( + errs, fmt.Errorf("Failed parsing boot_keygroup_interval: %s", err)) + } else { + c.BootGroupInterval = bgi + } + } + if c.BootCommand != nil { expSeq, err := GenerateExpressionSequence(c.FlatBootCommand()) if err != nil { @@ -55,6 +74,21 @@ func (c *VNCConfig) Prepare(ctx *interpolate.Context) (errs []error) { errs = append(errs, fmt.Errorf("A boot command cannot be used when vnc is disabled.")) } + + if c.RawBootKeyInterval == "" { + c.RawBootKeyInterval = "0ms" + } + + if c.RawBootKeyInterval != "" { + bki, err := time.ParseDuration(c.RawBootKeyInterval) + if err != nil { + errs = append( + errs, fmt.Errorf("Failed parsing boot_key_interval: %s", err)) + } else { + c.BootKeyInterval = bki + } + } + errs = append(errs, c.BootConfig.Prepare(ctx)...) return } diff --git a/common/bootcommand/pc_xt_driver.go b/common/bootcommand/pc_xt_driver.go index 7aca80852..2a3dcb746 100644 --- a/common/bootcommand/pc_xt_driver.go +++ b/common/bootcommand/pc_xt_driver.go @@ -38,13 +38,17 @@ func (sc *scancode) makeBreak() []string { // NewPCXTDriver creates a new boot command driver for VMs that expect PC-XT // keyboard codes. `send` should send its argument to the VM. `chunkSize` should // be the maximum number of keyboard codes to send to `send` at one time. -func NewPCXTDriver(send SendCodeFunc, chunkSize int) *pcXTDriver { +func NewPCXTDriver(send SendCodeFunc, chunkSize int, interval time.Duration) *pcXTDriver { // We delay (default 100ms) between each input event to allow for CPU or // network latency. See PackerKeyEnv for tuning. keyInterval := common.PackerKeyDefault if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil { keyInterval = delay } + // Override interval based on builder-specific override + if interval > time.Duration(0) { + keyInterval = interval + } // Scancodes reference: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html // diff --git a/common/bootcommand/pc_xt_driver_test.go b/common/bootcommand/pc_xt_driver_test.go index cd0e04ec8..5133f7e85 100644 --- a/common/bootcommand/pc_xt_driver_test.go +++ b/common/bootcommand/pc_xt_driver_test.go @@ -3,6 +3,7 @@ package bootcommand import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -79,7 +80,7 @@ func Test_pcxtSpecialOnOff(t *testing.T) { codes = c return nil } - d := NewPCXTDriver(sendCodes, -1) + d := NewPCXTDriver(sendCodes, -1, time.Duration(0)) seq, err := GenerateExpressionSequence(in) assert.NoError(t, err) err = seq.Do(context.Background(), d) @@ -95,7 +96,7 @@ func Test_pcxtSpecial(t *testing.T) { codes = c return nil } - d := NewPCXTDriver(sendCodes, -1) + d := NewPCXTDriver(sendCodes, -1, time.Duration(0)) seq, err := GenerateExpressionSequence(in) assert.NoError(t, err) err = seq.Do(context.Background(), d) @@ -114,10 +115,30 @@ func Test_flushes(t *testing.T) { actual = append(actual, c) return nil } - d := NewPCXTDriver(sendCodes, -1) + d := NewPCXTDriver(sendCodes, -1, time.Duration(0)) seq, err := GenerateExpressionSequence(in) assert.NoError(t, err) err = seq.Do(context.Background(), d) assert.NoError(t, err) assert.Equal(t, expected, actual) } + +func Test_KeyIntervalNotGiven(t *testing.T) { + var codes []string + sendCodes := func(c []string) error { + codes = c + return nil + } + d := NewPCXTDriver(sendCodes, -1, time.Duration(0)) + assert.Equal(t, d.interval, time.Duration(100)*time.Millisecond) +} + +func Test_KeyIntervalGiven(t *testing.T) { + var codes []string + sendCodes := func(c []string) error { + codes = c + return nil + } + d := NewPCXTDriver(sendCodes, -1, time.Duration(5000)*time.Millisecond) + assert.Equal(t, d.interval, time.Duration(5000)*time.Millisecond) +} diff --git a/common/bootcommand/vnc_driver.go b/common/bootcommand/vnc_driver.go index dfe75b9b2..027bcffca 100644 --- a/common/bootcommand/vnc_driver.go +++ b/common/bootcommand/vnc_driver.go @@ -25,13 +25,17 @@ type vncDriver struct { err error } -func NewVNCDriver(c VNCKeyEvent) *vncDriver { +func NewVNCDriver(c VNCKeyEvent, interval time.Duration) *vncDriver { // We delay (default 100ms) between each key event to allow for CPU or // network latency. See PackerKeyEnv for tuning. keyInterval := common.PackerKeyDefault if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil { keyInterval = delay } + // override interval based on builder-specific override. + if interval > time.Duration(0) { + keyInterval = interval + } // Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h sMap := make(map[string]uint32) diff --git a/common/bootcommand/vnc_driver_test.go b/common/bootcommand/vnc_driver_test.go index 11ca41669..75b585fce 100644 --- a/common/bootcommand/vnc_driver_test.go +++ b/common/bootcommand/vnc_driver_test.go @@ -3,6 +3,7 @@ package bootcommand import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -30,10 +31,22 @@ func Test_vncSpecialLookup(t *testing.T) { {0xFFE2, true}, } s := &sender{} - d := NewVNCDriver(s) + d := NewVNCDriver(s, time.Duration(0)) seq, err := GenerateExpressionSequence(in) assert.NoError(t, err) err = seq.Do(context.Background(), d) assert.NoError(t, err) assert.Equal(t, expected, s.e) } + +func Test_vncIntervalNotGiven(t *testing.T) { + s := &sender{} + d := NewVNCDriver(s, time.Duration(0)) + assert.Equal(t, d.interval, time.Duration(100)*time.Millisecond) +} + +func Test_vncIntervalGiven(t *testing.T) { + s := &sender{} + d := NewVNCDriver(s, time.Duration(5000)*time.Millisecond) + assert.Equal(t, d.interval, time.Duration(5000)*time.Millisecond) +} diff --git a/website/source/docs/builders/qemu.html.md.erb b/website/source/docs/builders/qemu.html.md.erb index 1d190b7ec..962b85c41 100644 --- a/website/source/docs/builders/qemu.html.md.erb +++ b/website/source/docs/builders/qemu.html.md.erb @@ -121,7 +121,7 @@ Linux server and have not enabled X11 forwarding (`ssh -X`). upstream issue which can be tracked [here](https://github.com/intel/haxm/issues/20). - -> The `hvf` accelerator is new and experimental as of + -> The `hvf` accelerator is new and experimental as of [QEMU 2.12.0](https://wiki.qemu.org/ChangeLog/2.12#Host_support). You may encounter issues unrelated to Packer when using it. You may need to add [ "-global", "virtio-pci.disable-modern=on" ] to `qemuargs` depending on the @@ -386,8 +386,20 @@ machine, simulating a human actually typing the keyboard. -> Keystrokes are typed as separate key up/down events over VNC with a default 100ms delay. The delay alleviates issues with latency and CPU -contention. For local builds you can tune this delay by specifying -e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command. +contention. You can tune this delay on a per-builder basis by specifying +"boot_key_interval" in your Packer template, for example: + +``` +{ + "builders": [ + { + "type": "qemu", + "boot_key_interval": "10ms" + ... + } + ] +} +``` <%= partial "partials/builders/boot-command" %> diff --git a/website/source/docs/builders/virtualbox-iso.html.md.erb b/website/source/docs/builders/virtualbox-iso.html.md.erb index 788f5e709..b97f9eb1c 100644 --- a/website/source/docs/builders/virtualbox-iso.html.md.erb +++ b/website/source/docs/builders/virtualbox-iso.html.md.erb @@ -337,8 +337,20 @@ template. The boot command is sent to the VM through the `VBoxManage` utility in as few invocations as possible. We send each character in groups of 25, with a default delay of 100ms between groups. The delay alleviates issues with latency and CPU -contention. If you notice missing keys, you can tune this delay by specifying e.g. -`PACKER_KEY_INTERVAL=500ms` to wait longer between each group of characters. +contention. If you notice missing keys, you can tune this delay by specifying +"boot_keygroup_interval" in your Packer template, for example: + +``` +{ + "builders": [ + { + "type": "virtualbox", + "boot_keygroup_interval": "500ms" + ... + } + ] +} +``` <%= partial "partials/builders/boot-command" %> diff --git a/website/source/docs/builders/virtualbox-ovf.html.md.erb b/website/source/docs/builders/virtualbox-ovf.html.md.erb index ae4317240..5981ad8cf 100644 --- a/website/source/docs/builders/virtualbox-ovf.html.md.erb +++ b/website/source/docs/builders/virtualbox-ovf.html.md.erb @@ -300,8 +300,20 @@ template. The boot command is sent to the VM through the `VBoxManage` utility in as few invocations as possible. We send each character in groups of 25, with a default delay of 100ms between groups. The delay alleviates issues with latency and CPU -contention. If you notice missing keys, you can tune this delay by specifying e.g. -`PACKER_KEY_INTERVAL=500ms` to wait longer between each group of characters. +contention. If you notice missing keys, you can tune this delay by specifying +"boot_keygroup_interval" in your Packer template, for example: + +``` +{ + "builders": [ + { + "type": "virtualbox", + "boot_keygroup_interval": "500ms", + ... + } + ] +} +``` <%= partial "partials/builders/boot-command" %> diff --git a/website/source/docs/builders/vmware-iso.html.md.erb b/website/source/docs/builders/vmware-iso.html.md.erb index b42259ee8..2dcbd36cf 100644 --- a/website/source/docs/builders/vmware-iso.html.md.erb +++ b/website/source/docs/builders/vmware-iso.html.md.erb @@ -412,7 +412,7 @@ builder. wish to bind to all interfaces use `0.0.0.0`. - `vnc_disable_password` (boolean) - Don't auto-generate a VNC password that - is used to secure the VNC communication with the VM. This must be set to + is used to secure the VNC communication with the VM. This must be set to `true` if building on ESXi 6.5 and 6.7 with VNC enabled. Defaults to `false`. @@ -439,8 +439,20 @@ machine, simulating a human actually typing the keyboard. -> Keystrokes are typed as separate key up/down events over VNC with a default 100ms delay. The delay alleviates issues with latency and CPU -contention. For local builds you can tune this delay by specifying -e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command. +contention. You can tune this delay on a per-builder basis by specifying +"boot_key_interval" in your Packer template, for example: + +``` +{ + "builders": [ + { + "type": "vmware-iso", + "boot_key_interval": "10ms" + ... + } + ] +} +``` <%= partial "partials/builders/boot-command" %> diff --git a/website/source/docs/builders/vmware-vmx.html.md.erb b/website/source/docs/builders/vmware-vmx.html.md.erb index 5ce8ef4e3..d597bb98d 100644 --- a/website/source/docs/builders/vmware-vmx.html.md.erb +++ b/website/source/docs/builders/vmware-vmx.html.md.erb @@ -215,8 +215,20 @@ machine, simulating a human actually typing the keyboard. -> Keystrokes are typed as separate key up/down events over VNC with a default 100ms delay. The delay alleviates issues with latency and CPU -contention. For local builds you can tune this delay by specifying -e.g. `PACKER_KEY_INTERVAL=10ms` to speed through the boot command. +contention. You can tune this delay on a per-builder basis by specifying +"boot_key_interval" in your Packer template, for example: + +``` +{ + "builders": [ + { + "type": "vmware-vmx", + "boot_key_interval": "10ms" + ... + } + ] +} +``` <%= partial "partials/builders/boot-command" %>