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