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 }