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 }