|
|
|
|
@ -12,6 +12,7 @@ type stepShutdown struct{}
|
|
|
|
|
|
|
|
|
|
func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
|
|
|
|
client := state.Get("client").(*DigitalOceanClient)
|
|
|
|
|
c := state.Get("config").(config)
|
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
|
dropletId := state.Get("droplet_id").(uint)
|
|
|
|
|
|
|
|
|
|
@ -19,30 +20,53 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
|
|
|
|
// of times because sometimes it says it completed when it actually
|
|
|
|
|
// did absolutely nothing (*ALAKAZAM!* magic!). We give up after
|
|
|
|
|
// a pretty arbitrary amount of time.
|
|
|
|
|
var err error
|
|
|
|
|
ui.Say("Gracefully shutting down droplet...")
|
|
|
|
|
for attempts := 1; attempts <= 10; attempts++ {
|
|
|
|
|
log.Printf("ShutdownDroplet attempt #%d...", attempts)
|
|
|
|
|
err := client.ShutdownDroplet(dropletId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
|
|
|
|
state.Put("error", err)
|
|
|
|
|
ui.Error(err.Error())
|
|
|
|
|
return multistep.ActionHalt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = waitForDropletState("off", dropletId, client, 20*time.Second)
|
|
|
|
|
if err == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := client.ShutdownDroplet(dropletId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// If we get an error the first time, actually report it
|
|
|
|
|
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
|
|
|
|
state.Put("error", err)
|
|
|
|
|
ui.Error(err.Error())
|
|
|
|
|
return multistep.ActionHalt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A channel we use as a flag to end our goroutines
|
|
|
|
|
done := make(chan struct{})
|
|
|
|
|
shutdownRetryDone := make(chan struct{})
|
|
|
|
|
|
|
|
|
|
// Make sure we wait for the shutdown retry goroutine to end
|
|
|
|
|
// before moving on.
|
|
|
|
|
defer func() {
|
|
|
|
|
close(done)
|
|
|
|
|
<-shutdownRetryDone
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
// Start a goroutine that just keeps trying to shut down the
|
|
|
|
|
// droplet.
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(shutdownRetryDone)
|
|
|
|
|
|
|
|
|
|
for attempts := 2; attempts > 0; attempts++ {
|
|
|
|
|
log.Printf("ShutdownDroplet attempt #%d...", attempts)
|
|
|
|
|
err := client.ShutdownDroplet(dropletId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("Shutdown retry error: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case <-done:
|
|
|
|
|
return
|
|
|
|
|
case <-time.After(20 * time.Second):
|
|
|
|
|
// Retry!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
err = waitForDropletState("off", dropletId, client, 2 * time.Minute)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("Error waiting for graceful off: %s", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return multistep.ActionContinue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|