packer-cn/common/bootcommand/config.go

217 lines
7.5 KiB
Go

//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:
//
// - `<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:
//
// ``` json
// "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:
//
// ``` json
// [
// "<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.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
}