diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index d1d91d79f..dfcc9357f 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -388,7 +388,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe steps = append(steps, new(stepConfigureVNC), steprun, - &stepBootWait{}, &stepTypeBootCommand{}, ) diff --git a/builder/qemu/step_boot_wait.go b/builder/qemu/step_boot_wait.go deleted file mode 100644 index 0c3935004..000000000 --- a/builder/qemu/step_boot_wait.go +++ /dev/null @@ -1,27 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "time" - - "github.com/hashicorp/packer/helper/multistep" - "github.com/hashicorp/packer/packer" -) - -// stepBootWait waits the configured time period. -type stepBootWait struct{} - -func (s *stepBootWait) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packer.Ui) - - if int64(config.bootWait) > 0 { - ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait)) - time.Sleep(config.bootWait) - } - - return multistep.ActionContinue -} - -func (s *stepBootWait) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_type_boot_command.go b/builder/qemu/step_type_boot_command.go index f1dc895e9..a0cbb7a90 100644 --- a/builder/qemu/step_type_boot_command.go +++ b/builder/qemu/step_type_boot_command.go @@ -5,15 +5,10 @@ import ( "fmt" "log" "net" - "regexp" - "strings" "time" - "unicode" - "unicode/utf8" - - "os" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/boot_command" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" @@ -40,13 +35,24 @@ type bootCommandTemplateData struct { // type stepTypeBootCommand struct{} -func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { +func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { config := state.Get("config").(*Config) debug := state.Get("debug").(bool) httpPort := state.Get("http_port").(uint) ui := state.Get("ui").(packer.Ui) vncPort := state.Get("vnc_port").(uint) + // Wait the for the vm to boot. + if int64(config.bootWait) > 0 { + ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait.String())) + select { + case <-time.After(config.bootWait): + break + case <-ctx.Done(): + return multistep.ActionHalt + } + } + var pauseFn multistep.DebugPauseFn if debug { pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn) @@ -76,16 +82,18 @@ func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m hostIP := "10.0.2.2" common.SetHTTPIP(hostIP) - ctx := config.ctx - ctx.Data = &bootCommandTemplateData{ + configCtx := config.ctx + configCtx.Data = &bootCommandTemplateData{ hostIP, httpPort, config.VMName, } + d := bootcommand.NewVNCDriver(c) + ui.Say("Typing the boot command over VNC...") for i, command := range config.BootCommand { - command, err := interpolate.Render(command, &ctx) + command, err := interpolate.Render(command, &configCtx) if err != nil { err := fmt.Errorf("Error preparing boot command: %s", err) state.Put("error", err) @@ -93,259 +101,27 @@ func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m return multistep.ActionHalt } - // Check for interrupts between typing things so we can cancel - // since this isn't the fastest thing. - if _, ok := state.GetOk(multistep.StateCancelled); ok { + seq, err := bootcommand.GenerateExpressionSequence(command) + if err != nil { + err := fmt.Errorf("Error generating boot command: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if err := seq.Do(ctx, d); err != nil { + err := fmt.Errorf("Error running boot command: %s", err) + state.Put("error", err) + ui.Error(err.Error()) return multistep.ActionHalt } if pauseFn != nil { pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state) } - - vncSendString(c, command) } return multistep.ActionContinue } 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 - special := make(map[string]uint32) - special[""] = 0xFF08 - special[""] = 0xFFFF - special[""] = 0xFF0D - special[""] = 0xFF1B - special[""] = 0xFFBE - special[""] = 0xFFBF - special[""] = 0xFFC0 - special[""] = 0xFFC1 - special[""] = 0xFFC2 - special[""] = 0xFFC3 - special[""] = 0xFFC4 - special[""] = 0xFFC5 - special[""] = 0xFFC6 - special[""] = 0xFFC7 - special[""] = 0xFFC8 - special[""] = 0xFFC9 - special[""] = 0xFF0D - special[""] = 0xFF09 - special[""] = 0xFF52 - special[""] = 0xFF54 - special[""] = 0xFF51 - special[""] = 0xFF53 - special[""] = 0x020 - special[""] = 0xFF63 - special[""] = 0xFF50 - special[""] = 0xFF57 - special[""] = 0xFF55 - special[""] = 0xFF56 - special[""] = 0xFFE9 - special[""] = 0xFFE3 - special[""] = 0xFFE1 - special[""] = 0xFFEA - special[""] = 0xFFE4 - special[""] = 0xFFE2 - - shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" - waitRe := regexp.MustCompile(`^`) - - // 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 - } - - // TODO(mitchellh): Ripe for optimizations of some point, perhaps. - for len(original) > 0 { - var keyCode uint32 - keyShift := false - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - keyCode = special[""] - original = original[len(""):] - log.Printf("Special code '' found, replacing with: %d", keyCode) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - continue - } - - if strings.HasPrefix(original, "") { - log.Printf("Special code '' found, sleeping one second") - time.Sleep(1 * time.Second) - original = original[len(""):] - continue - } - - if strings.HasPrefix(original, "") { - log.Printf("Special code '' found, sleeping 5 seconds") - time.Sleep(5 * time.Second) - original = original[len(""):] - continue - } - - if strings.HasPrefix(original, "") { - log.Printf("Special code '' found, sleeping 10 seconds") - time.Sleep(10 * time.Second) - original = original[len(""):] - continue - } - - waitMatch := waitRe.FindStringSubmatch(original) - if len(waitMatch) > 1 { - log.Printf("Special code %s found, sleeping", waitMatch[0]) - if dt, err := time.ParseDuration(waitMatch[1]); err == nil { - time.Sleep(dt) - original = original[len(waitMatch[0]):] - continue - } - } - - for specialCode, specialValue := range special { - if strings.HasPrefix(original, specialCode) { - log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue) - keyCode = specialValue - original = original[len(specialCode):] - break - } - } - - if keyCode == 0 { - r, size := utf8.DecodeRuneInString(original) - original = original[size:] - keyCode = uint32(r) - keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) - - log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift) - } - - if keyShift { - c.KeyEvent(KeyLeftShift, true) - } - - c.KeyEvent(keyCode, true) - time.Sleep(keyInterval) - - c.KeyEvent(keyCode, false) - time.Sleep(keyInterval) - - if keyShift { - c.KeyEvent(KeyLeftShift, false) - } - time.Sleep(keyInterval) - } -} diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index ca46b5d88..6cfb196e3 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -129,10 +129,6 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - if pauseFn != nil { - pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state) - } - seq, err := bootcommand.GenerateExpressionSequence(command) if err != nil { err := fmt.Errorf("Error generating boot command: %s", err) @@ -147,6 +143,11 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) ui.Error(err.Error()) return multistep.ActionHalt } + + if pauseFn != nil { + pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state) + } + } return multistep.ActionContinue