//go:generate struct-markdown package bootcommand import ( "fmt" "strings" "time" "github.com/hashicorp/packer/template/interpolate" ) // 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: // // - `` - Backspace // // - `` - Delete // // - ` ` - Simulates an actual "enter" or "return" keypress. // // - `` - Simulates pressing the escape key. // // - `` - Simulates pressing the tab key. // // - ` - ` - Simulates pressing a function key. // // - ` ` - Simulates pressing an arrow key. // // - `` - Simulates pressing the spacebar. // // - `` - Simulates pressing the insert key. // // - ` ` - Simulates pressing the home and end keys. // // - ` ` - Simulates pressing the page up and page down // keys. // // - `` - Simulates pressing the Menu key. // // - ` ` - Simulates pressing the alt key. // // - ` ` - Simulates pressing the ctrl key. // // - ` ` - Simulates pressing the shift key. // // - ` ` - Simulates pressing the ⌘ or Windows key. // // - ` ` - 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. // // - `` - 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 `` or ``. // // - ` ` - Any printable keyboard character, and of these // "special" expressions, with the exception of the `` types, can // also be toggled on or off. For example, to simulate ctrl+c, use // `c`. Be sure to release them, otherwise they // will be held down until the machine reboots. To hold the `c` key down, // you would use ``. Likewise, `` 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: // // ``` json // "boot_command": [ // "", // " ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg" // ] // ``` // // The example shown below is a working boot command used to start an Ubuntu // 12.04 installer: // // ``` json // [ // "", // "/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 -- " // ] // ``` // For more examples of various boot commands, see the sample projects from our // [community templates page](/community-tools.html#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. RawBootGroupInterval string `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. RawBootWait string `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"` BootGroupInterval time.Duration `` BootWait time.Duration `` } // 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 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 { errs = append( errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) } else { c.BootWait = bw } } 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 { 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.")) } 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 }