Commit 03e22c63 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/vmware: graceful shutdown

parent c559ec7d
...@@ -29,12 +29,14 @@ type config struct { ...@@ -29,12 +29,14 @@ type config struct {
BootCommand []string `mapstructure:"boot_command"` BootCommand []string `mapstructure:"boot_command"`
BootWait time.Duration BootWait time.Duration
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
ShutdownTimeout time.Duration
SSHUser string `mapstructure:"ssh_username"` SSHUser string `mapstructure:"ssh_username"`
SSHPassword string `mapstructure:"ssh_password"` SSHPassword string `mapstructure:"ssh_password"`
SSHWaitTimeout time.Duration SSHWaitTimeout time.Duration
RawBootWait string `mapstructure:"boot_wait"` RawBootWait string `mapstructure:"boot_wait"`
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"` RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
} }
func (b *Builder) Prepare(raw interface{}) (err error) { func (b *Builder) Prepare(raw interface{}) (err error) {
...@@ -73,6 +75,15 @@ func (b *Builder) Prepare(raw interface{}) (err error) { ...@@ -73,6 +75,15 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
} }
} }
if b.config.RawShutdownTimeout == "" {
b.config.RawShutdownTimeout = "5m"
}
b.config.ShutdownTimeout, err = time.ParseDuration(b.config.RawShutdownTimeout)
if err != nil {
errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err))
}
if b.config.RawSSHWaitTimeout == "" { if b.config.RawSSHWaitTimeout == "" {
b.config.RawSSHWaitTimeout = "20m" b.config.RawSSHWaitTimeout = "20m"
} }
...@@ -104,6 +115,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook) packer.Artifact { ...@@ -104,6 +115,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook) packer.Artifact {
&stepTypeBootCommand{}, &stepTypeBootCommand{},
&stepWaitForSSH{}, &stepWaitForSSH{},
&stepProvision{}, &stepProvision{},
&stepShutdown{},
} }
// Setup the state bag // Setup the state bag
......
package vmware
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
// This step shuts down the machine. It first attempts to do so gracefully,
// but ultimately forcefully shuts it down if that fails.
//
// Uses:
// communicator packer.Communicator
// config *config
// driver Driver
// ui packer.Ui
// vmx_path string
//
// Produces:
// <nothing>
type stepShutdown struct{}
func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
if config.ShutdownCommand != "" {
ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", config.ShutdownCommand)
cmd := &packer.RemoteCmd{Command: config.ShutdownCommand}
if err := comm.Start(cmd); err != nil {
ui.Error(fmt.Sprintf("Failed to send shutdown command: %s", err))
return multistep.ActionHalt
}
// Wait for the command to run
cmd.Wait()
// Wait for the machine to actually shut down
log.Printf("Waiting max %s for shutdown to complete", config.ShutdownTimeout)
shutdownTimer := time.After(config.ShutdownTimeout)
for {
running, _ := driver.IsRunning(vmxPath)
if !running {
break
}
select {
case <-shutdownTimer:
ui.Error("Timeout while waiting for machine to shut down.")
return multistep.ActionHalt
default:
time.Sleep(1 * time.Second)
}
}
} else {
if err := driver.Stop(vmxPath); err != nil {
ui.Error(fmt.Sprintf("Error stopping VM: %s", err))
return multistep.ActionHalt
}
}
log.Println("VM shut down.")
return multistep.ActionContinue
}
func (s *stepShutdown) Cleanup(state map[string]interface{}) {}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment