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