1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
//go:generate struct-markdown
package bootcommand
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
)
// PackerKeyEnv is used to specify the key interval (delay) between keystrokes
// sent to the VM, typically in boot commands. This is to prevent host CPU
// utilization from causing key presses to be skipped or repeated incorrectly.
const PackerKeyEnv = "PACKER_KEY_INTERVAL"
// PackerKeyDefault 100ms is appropriate for shared build infrastructure while a
// shorter delay (e.g. 10ms) can be used on a workstation. See PackerKeyEnv.
const PackerKeyDefault = 100 * time.Millisecond
// The boot configuration is very important: `boot_command` specifies the keys
// to type when the virtual machine is first booted in order to start the OS
// installer. This command is typed after boot_wait, which gives the virtual
// machine some time to actually load.
//
// The boot_command is an array of strings. The strings are all typed in
// sequence. It is an array only to improve readability within the template.
//
// There are a set of special keys available. If these are in your boot
// command, they will be replaced by the proper key:
//
// - `<bs>` - Backspace
//
// - `<del>` - Delete
//
// - `<enter> <return>` - Simulates an actual "enter" or "return" keypress.
//
// - `<esc>` - Simulates pressing the escape key.
//
// - `<tab>` - Simulates pressing the tab key.
//
// - `<f1> - <f12>` - Simulates pressing a function key.
//
// - `<up> <down> <left> <right>` - Simulates pressing an arrow key.
//
// - `<spacebar>` - Simulates pressing the spacebar.
//
// - `<insert>` - Simulates pressing the insert key.
//
// - `<home> <end>` - Simulates pressing the home and end keys.
//
// - `<pageUp> <pageDown>` - Simulates pressing the page up and page down
// keys.
//
// - `<menu>` - Simulates pressing the Menu key.
//
// - `<leftAlt> <rightAlt>` - Simulates pressing the alt key.
//
// - `<leftCtrl> <rightCtrl>` - Simulates pressing the ctrl key.
//
// - `<leftShift> <rightShift>` - Simulates pressing the shift key.
//
// - `<leftSuper> <rightSuper>` - Simulates pressing the ⌘ or Windows key.
//
// - `<wait> <wait5> <wait10>` - Adds a 1, 5 or 10 second pause before
// sending any additional keys. This is useful if you have to generally
// wait for the UI to update before typing more.
//
// - `<waitXX>` - Add an arbitrary pause before sending any additional keys.
// The format of `XX` is a sequence of positive decimal numbers, each with
// optional fraction and a unit suffix, such as `300ms`, `1.5h` or `2h45m`.
// Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For
// example `<wait10m>` or `<wait1m20s>`.
//
// - `<XXXOn> <XXXOff>` - Any printable keyboard character, and of these
// "special" expressions, with the exception of the `<wait>` types, can
// also be toggled on or off. For example, to simulate ctrl+c, use
// `<leftCtrlOn>c<leftCtrlOff>`. Be sure to release them, otherwise they
// will be held down until the machine reboots. To hold the `c` key down,
// you would use `<cOn>`. Likewise, `<cOff>` to release.
//
// - `{{ .HTTPIP }} {{ .HTTPPort }}` - The IP and port, respectively of an
// HTTP server that is started serving the directory specified by the
// `http_directory` configuration parameter. If `http_directory` isn't
// specified, these will be blank!
//
// - `{{ .Name }}` - The name of the VM.
//
// Example boot command. This is actually a working boot command used to start an
// CentOS 6.4 installer:
//
// In JSON:
//
// ```json
// "boot_command": [
// "<tab><wait>",
// " ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg<enter>"
// ]
// ```
//
// In HCL2:
//
// ```hcl
// boot_command = [
// "<tab><wait>",
// " ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg<enter>"
// ]
// ```
//
// The example shown below is a working boot command used to start an Ubuntu
// 12.04 installer:
//
// In JSON:
//
// ```json
// "boot_command": [
// "<esc><esc><enter><wait>",
// "/install/vmlinuz noapic ",
// "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
// "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
// "hostname={{ .Name }} ",
// "fb=false debconf/frontend=noninteractive ",
// "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
// "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
// "initrd=/install/initrd.gz -- <enter>"
// ]
// ```
//
// In HCL2:
//
// ```hcl
// boot_command = [
// "<esc><esc><enter><wait>",
// "/install/vmlinuz noapic ",
// "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
// "debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
// "hostname={{ .Name }} ",
// "fb=false debconf/frontend=noninteractive ",
// "keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
// "keyboard-configuration/variant=USA console-setup/ask_detect=false ",
// "initrd=/install/initrd.gz -- <enter>"
// ]
// ```
//
// For more examples of various boot commands, see the sample projects from our
// [community templates page](/community-tools#templates).
type BootConfig struct {
// Time to wait after sending a group of key pressses. 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, a sensible default value is picked depending on
// the builder type.
BootGroupInterval time.Duration `mapstructure:"boot_keygroup_interval"`
// 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
// `10s` or 10 seconds. To set boot_wait to 0s, use a negative number, such
// as "-1s"
BootWait time.Duration `mapstructure:"boot_wait"`
// 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.
BootCommand []string `mapstructure:"boot_command"`
}
// The boot command "typed" character for character over a VNC connection to
// the 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.
// You can tune this delay on a per-builder basis by specifying
// "boot_key_interval" in your Packer template.
type VNCConfig struct {
BootConfig `mapstructure:",squash"`
// Whether to create a VNC connection or not. A boot_command cannot be used
// when this is true. Defaults to false.
DisableVNC bool `mapstructure:"disable_vnc"`
// Time in ms to wait between each key press
BootKeyInterval time.Duration `mapstructure:"boot_key_interval"`
}
func (c *BootConfig) Prepare(ctx *interpolate.Context) (errs []error) {
if c.BootWait == 0 {
c.BootWait = 10 * time.Second
}
if c.BootCommand != nil {
expSeq, err := GenerateExpressionSequence(c.FlatBootCommand())
if err != nil {
errs = append(errs, err)
} else if vErrs := expSeq.Validate(); vErrs != nil {
errs = append(errs, vErrs...)
}
}
return
}
func (c *BootConfig) FlatBootCommand() string {
return strings.Join(c.BootCommand, "")
}
func (c *VNCConfig) Prepare(ctx *interpolate.Context) (errs []error) {
if len(c.BootCommand) > 0 && c.DisableVNC {
errs = append(errs,
fmt.Errorf("A boot command cannot be used when vnc is disabled."))
}
errs = append(errs, c.BootConfig.Prepare(ctx)...)
return
}
|