github.com/alouche/packer@v0.3.7/builder/vmware/step_shutdown.go (about) 1 package vmware 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "github.com/mitchellh/multistep" 8 "github.com/mitchellh/packer/packer" 9 "log" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "time" 14 ) 15 16 // This step shuts down the machine. It first attempts to do so gracefully, 17 // but ultimately forcefully shuts it down if that fails. 18 // 19 // Uses: 20 // communicator packer.Communicator 21 // config *config 22 // driver Driver 23 // ui packer.Ui 24 // vmx_path string 25 // 26 // Produces: 27 // <nothing> 28 type stepShutdown struct{} 29 30 func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { 31 comm := state.Get("communicator").(packer.Communicator) 32 config := state.Get("config").(*config) 33 driver := state.Get("driver").(Driver) 34 ui := state.Get("ui").(packer.Ui) 35 vmxPath := state.Get("vmx_path").(string) 36 37 if config.ShutdownCommand != "" { 38 ui.Say("Gracefully halting virtual machine...") 39 log.Printf("Executing shutdown command: %s", config.ShutdownCommand) 40 41 var stdout, stderr bytes.Buffer 42 cmd := &packer.RemoteCmd{ 43 Command: config.ShutdownCommand, 44 Stdout: &stdout, 45 Stderr: &stderr, 46 } 47 if err := comm.Start(cmd); err != nil { 48 err := fmt.Errorf("Failed to send shutdown command: %s", err) 49 state.Put("error", err) 50 ui.Error(err.Error()) 51 return multistep.ActionHalt 52 } 53 54 // Wait for the command to run 55 cmd.Wait() 56 57 // If the command failed to run, notify the user in some way. 58 if cmd.ExitStatus != 0 { 59 state.Put("error", fmt.Errorf( 60 "Shutdown command has non-zero exit status.\n\nStdout: %s\n\nStderr: %s", 61 stdout.String(), stderr.String())) 62 return multistep.ActionHalt 63 } 64 65 log.Printf("Shutdown stdout: %s", stdout.String()) 66 log.Printf("Shutdown stderr: %s", stderr.String()) 67 68 // Wait for the machine to actually shut down 69 log.Printf("Waiting max %s for shutdown to complete", config.shutdownTimeout) 70 shutdownTimer := time.After(config.shutdownTimeout) 71 for { 72 running, _ := driver.IsRunning(vmxPath) 73 if !running { 74 break 75 } 76 77 select { 78 case <-shutdownTimer: 79 err := errors.New("Timeout while waiting for machine to shut down.") 80 state.Put("error", err) 81 ui.Error(err.Error()) 82 return multistep.ActionHalt 83 default: 84 time.Sleep(1 * time.Second) 85 } 86 } 87 } else { 88 if err := driver.Stop(vmxPath); err != nil { 89 err := fmt.Errorf("Error stopping VM: %s", err) 90 state.Put("error", err) 91 ui.Error(err.Error()) 92 return multistep.ActionHalt 93 } 94 } 95 96 ui.Message("Waiting for VMware to clean up after itself...") 97 lockPattern := filepath.Join(config.OutputDir, "*.lck") 98 timer := time.After(15 * time.Second) 99 LockWaitLoop: 100 for { 101 locks, err := filepath.Glob(lockPattern) 102 if err == nil { 103 if len(locks) == 0 { 104 log.Println("No more lock files found. VMware is clean.") 105 break 106 } 107 108 if len(locks) == 1 && strings.HasSuffix(locks[0], ".vmx.lck") { 109 log.Println("Only waiting on VMX lock. VMware is clean.") 110 break 111 } 112 113 log.Printf("Waiting on lock files: %#v", locks) 114 } 115 116 select { 117 case <-timer: 118 log.Println("Reached timeout on waiting for clean VMware. Assuming clean.") 119 break LockWaitLoop 120 case <-time.After(1 * time.Second): 121 } 122 } 123 124 if runtime.GOOS == "windows" { 125 // Windows takes a while to yield control of the files when the 126 // process is exiting. We just sleep here. In the future, it'd be 127 // nice to find a better solution to this. 128 time.Sleep(5 * time.Second) 129 } 130 131 log.Println("VM shut down.") 132 return multistep.ActionContinue 133 } 134 135 func (s *stepShutdown) Cleanup(state multistep.StateBag) {}