github.com/mitchellh/packer@v1.3.2/builder/vmware/common/step_shutdown.go (about)

     1  package common
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"log"
     9  	"regexp"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/hashicorp/packer/helper/multistep"
    14  	"github.com/hashicorp/packer/packer"
    15  )
    16  
    17  // This step shuts down the machine. It first attempts to do so gracefully,
    18  // but ultimately forcefully shuts it down if that fails.
    19  //
    20  // Uses:
    21  //   communicator packer.Communicator
    22  //   dir OutputDir
    23  //   driver Driver
    24  //   ui     packer.Ui
    25  //   vmx_path string
    26  //
    27  // Produces:
    28  //   <nothing>
    29  type StepShutdown struct {
    30  	Command string
    31  	Timeout time.Duration
    32  
    33  	// Set this to true if we're testing
    34  	Testing bool
    35  }
    36  
    37  func (s *StepShutdown) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    38  	comm := state.Get("communicator").(packer.Communicator)
    39  	dir := state.Get("dir").(OutputDir)
    40  	driver := state.Get("driver").(Driver)
    41  	ui := state.Get("ui").(packer.Ui)
    42  	vmxPath := state.Get("vmx_path").(string)
    43  
    44  	if s.Command != "" {
    45  		ui.Say("Gracefully halting virtual machine...")
    46  		log.Printf("Executing shutdown command: %s", s.Command)
    47  
    48  		var stdout, stderr bytes.Buffer
    49  		cmd := &packer.RemoteCmd{
    50  			Command: s.Command,
    51  			Stdout:  &stdout,
    52  			Stderr:  &stderr,
    53  		}
    54  		if err := comm.Start(cmd); err != nil {
    55  			err := fmt.Errorf("Failed to send shutdown command: %s", err)
    56  			state.Put("error", err)
    57  			ui.Error(err.Error())
    58  			return multistep.ActionHalt
    59  		}
    60  
    61  		// Wait for the machine to actually shut down
    62  		log.Printf("Waiting max %s for shutdown to complete", s.Timeout)
    63  		shutdownTimer := time.After(s.Timeout)
    64  		for {
    65  			running, _ := driver.IsRunning(vmxPath)
    66  			if !running {
    67  				break
    68  			}
    69  
    70  			select {
    71  			case <-shutdownTimer:
    72  				log.Printf("Shutdown stdout: %s", stdout.String())
    73  				log.Printf("Shutdown stderr: %s", stderr.String())
    74  				err := errors.New("Timeout while waiting for machine to shut down.")
    75  				state.Put("error", err)
    76  				ui.Error(err.Error())
    77  				return multistep.ActionHalt
    78  			default:
    79  				time.Sleep(150 * time.Millisecond)
    80  			}
    81  		}
    82  	} else {
    83  		ui.Say("Forcibly halting virtual machine...")
    84  		if err := driver.Stop(vmxPath); err != nil {
    85  			err := fmt.Errorf("Error stopping VM: %s", err)
    86  			state.Put("error", err)
    87  			ui.Error(err.Error())
    88  			return multistep.ActionHalt
    89  		}
    90  	}
    91  
    92  	ui.Message("Waiting for VMware to clean up after itself...")
    93  	lockRegex := regexp.MustCompile(`(?i)\.lck$`)
    94  	timer := time.After(120 * time.Second)
    95  LockWaitLoop:
    96  	for {
    97  		files, err := dir.ListFiles()
    98  		if err != nil {
    99  			log.Printf("Error listing files in outputdir: %s", err)
   100  		} else {
   101  			var locks []string
   102  			for _, file := range files {
   103  				if lockRegex.MatchString(file) {
   104  					locks = append(locks, file)
   105  				}
   106  			}
   107  
   108  			if len(locks) == 0 {
   109  				log.Println("No more lock files found. VMware is clean.")
   110  				break
   111  			}
   112  
   113  			if len(locks) == 1 && strings.HasSuffix(locks[0], ".vmx.lck") {
   114  				log.Println("Only waiting on VMX lock. VMware is clean.")
   115  				break
   116  			}
   117  
   118  			log.Printf("Waiting on lock files: %#v", locks)
   119  		}
   120  
   121  		select {
   122  		case <-timer:
   123  			log.Println("Reached timeout on waiting for clean VMware. Assuming clean.")
   124  			break LockWaitLoop
   125  		case <-time.After(150 * time.Millisecond):
   126  		}
   127  	}
   128  
   129  	if !s.Testing {
   130  		// Windows takes a while to yield control of the files when the
   131  		// process is exiting. Ubuntu and OS X will yield control of the files
   132  		// but VMWare may overwrite the VMX cleanup steps that run after this,
   133  		// so we wait to ensure VMWare has exited and flushed the VMX.
   134  
   135  		// We just sleep here. In the future, it'd be nice to find a better
   136  		// solution to this.
   137  		time.Sleep(5 * time.Second)
   138  	}
   139  
   140  	log.Println("VM shut down.")
   141  	return multistep.ActionContinue
   142  }
   143  
   144  func (s *StepShutdown) Cleanup(state multistep.StateBag) {}