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