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) {}