Commit 82b46816 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/digitalocean: retry power off a number of times

See comment.
parent 306ebcf0
...@@ -16,7 +16,7 @@ func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction { ...@@ -16,7 +16,7 @@ func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Waiting for droplet to become active...") ui.Say("Waiting for droplet to become active...")
err := waitForDropletState("active", dropletId, client, c) err := waitForDropletState("active", dropletId, client, c.stateTimeout)
if err != nil { if err != nil {
err := fmt.Errorf("Error waiting for droplet to become active: %s", err) err := fmt.Errorf("Error waiting for droplet to become active: %s", err)
state.Put("error", err) state.Put("error", err)
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"log" "log"
"time"
) )
type stepPowerOff struct{} type stepPowerOff struct{}
...@@ -14,10 +15,27 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction { ...@@ -14,10 +15,27 @@ func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(uint) dropletId := state.Get("droplet_id").(uint)
// Poweroff the droplet so it can be snapshot // Gracefully power off the droplet. We have to retry this a number
err := client.PowerOffDroplet(dropletId) // 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("PowerOffDroplet attempt #%d...", attempts)
err := client.PowerOffDroplet(dropletId)
if err != nil {
err := fmt.Errorf("Error powering off 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 { if err != nil {
err := fmt.Errorf("Error powering off droplet: %s", err) err := fmt.Errorf("Error waiting for droplet to become 'off': %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
......
...@@ -22,8 +22,7 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction { ...@@ -22,8 +22,7 @@ func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt return multistep.ActionHalt
} }
ui.Say("Waiting for droplet to shutdown...") err = waitForDropletState("off", dropletId, client, c.stateTimeout)
err = waitForDropletState("off", dropletId, client, c)
if err != nil { if err != nil {
err := fmt.Errorf("Error waiting for droplet to become 'off': %s", err) err := fmt.Errorf("Error waiting for droplet to become 'off': %s", err)
state.Put("error", err) state.Put("error", err)
......
...@@ -26,7 +26,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { ...@@ -26,7 +26,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
} }
ui.Say("Waiting for snapshot to complete...") ui.Say("Waiting for snapshot to complete...")
err = waitForDropletState("active", dropletId, client, c) err = waitForDropletState("active", dropletId, client, c.stateTimeout)
if err != nil { if err != nil {
err := fmt.Errorf("Error waiting for snapshot to complete: %s", err) err := fmt.Errorf("Error waiting for snapshot to complete: %s", err)
state.Put("error", err) state.Put("error", err)
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
// waitForState simply blocks until the droplet is in // waitForState simply blocks until the droplet is in
// a state we expect, while eventually timing out. // a state we expect, while eventually timing out.
func waitForDropletState(desiredState string, dropletId uint, client *DigitalOceanClient, c config) error { func waitForDropletState(desiredState string, dropletId uint, client *DigitalOceanClient, timeout time.Duration) error {
result := make(chan error, 1) result := make(chan error, 1)
go func() { go func() {
attempts := 0 attempts := 0
...@@ -32,11 +32,11 @@ func waitForDropletState(desiredState string, dropletId uint, client *DigitalOce ...@@ -32,11 +32,11 @@ func waitForDropletState(desiredState string, dropletId uint, client *DigitalOce
} }
}() }()
log.Printf("Waiting for up to %s for droplet to become %s", c.RawStateTimeout, desiredState) log.Printf("Waiting for up to %d seconds for droplet to become %s", timeout, desiredState)
select { select {
case err := <-result: case err := <-result:
return err return err
case <-time.After(c.stateTimeout): case <-time.After(timeout):
err := fmt.Errorf("Timeout while waiting to for droplet to become '%s'", desiredState) err := fmt.Errorf("Timeout while waiting to for droplet to become '%s'", desiredState)
return err return err
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment