github.com/rothwerx/packer@v0.9.0/builder/digitalocean/step_shutdown.go (about)

     1  package digitalocean
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/digitalocean/godo"
     9  	"github.com/mitchellh/multistep"
    10  	"github.com/mitchellh/packer/packer"
    11  )
    12  
    13  type stepShutdown struct{}
    14  
    15  func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
    16  	client := state.Get("client").(*godo.Client)
    17  	ui := state.Get("ui").(packer.Ui)
    18  	dropletId := state.Get("droplet_id").(int)
    19  
    20  	// Gracefully power off the droplet. We have to retry this a number
    21  	// of times because sometimes it says it completed when it actually
    22  	// did absolutely nothing (*ALAKAZAM!* magic!). We give up after
    23  	// a pretty arbitrary amount of time.
    24  	ui.Say("Gracefully shutting down droplet...")
    25  	_, _, err := client.DropletActions.Shutdown(dropletId)
    26  	if err != nil {
    27  		// If we get an error the first time, actually report it
    28  		err := fmt.Errorf("Error shutting down droplet: %s", err)
    29  		state.Put("error", err)
    30  		ui.Error(err.Error())
    31  		return multistep.ActionHalt
    32  	}
    33  
    34  	// A channel we use as a flag to end our goroutines
    35  	done := make(chan struct{})
    36  	shutdownRetryDone := make(chan struct{})
    37  
    38  	// Make sure we wait for the shutdown retry goroutine to end
    39  	// before moving on.
    40  	defer func() {
    41  		close(done)
    42  		<-shutdownRetryDone
    43  	}()
    44  
    45  	// Start a goroutine that just keeps trying to shut down the
    46  	// droplet.
    47  	go func() {
    48  		defer close(shutdownRetryDone)
    49  
    50  		for attempts := 2; attempts > 0; attempts++ {
    51  			log.Printf("ShutdownDroplet attempt #%d...", attempts)
    52  			_, _, err := client.DropletActions.Shutdown(dropletId)
    53  			if err != nil {
    54  				log.Printf("Shutdown retry error: %s", err)
    55  			}
    56  
    57  			select {
    58  			case <-done:
    59  				return
    60  			case <-time.After(20 * time.Second):
    61  				// Retry!
    62  			}
    63  		}
    64  	}()
    65  
    66  	err = waitForDropletState("off", dropletId, client, 2*time.Minute)
    67  	if err != nil {
    68  		// If we get an error the first time, actually report it
    69  		err := fmt.Errorf("Error shutting down droplet: %s", err)
    70  		state.Put("error", err)
    71  		ui.Error(err.Error())
    72  		return multistep.ActionHalt
    73  	}
    74  
    75  	if err := waitForDropletUnlocked(client, dropletId, 4*time.Minute); err != nil {
    76  		// If we get an error the first time, actually report it
    77  		err := fmt.Errorf("Error shutting down droplet: %s", err)
    78  		state.Put("error", err)
    79  		ui.Error(err.Error())
    80  		return multistep.ActionHalt
    81  	}
    82  
    83  	return multistep.ActionContinue
    84  }
    85  
    86  func (s *stepShutdown) Cleanup(state multistep.StateBag) {
    87  	// no cleanup
    88  }