github.com/Hashicorp/packer@v1.3.2/builder/digitalocean/step_shutdown.go (about)

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