2013-08-24 07:04:51 -04:00
|
|
|
package digitalocean
|
|
|
|
|
|
|
|
import (
|
2017-04-08 15:52:57 -04:00
|
|
|
"context"
|
2013-08-24 07:04:51 -04:00
|
|
|
"fmt"
|
2013-09-05 01:14:30 -04:00
|
|
|
"log"
|
2013-09-05 01:26:05 -04:00
|
|
|
"time"
|
2014-09-03 17:37:33 -04:00
|
|
|
|
2015-06-10 17:02:06 -04:00
|
|
|
"github.com/digitalocean/godo"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2020-11-17 19:31:03 -05:00
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/multistep"
|
2013-08-24 07:04:51 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type stepShutdown struct{}
|
|
|
|
|
2019-03-29 11:50:02 -04:00
|
|
|
func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
2015-06-10 17:02:06 -04:00
|
|
|
client := state.Get("client").(*godo.Client)
|
2018-09-18 05:36:21 -04:00
|
|
|
c := state.Get("config").(*Config)
|
2013-08-31 15:25:08 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
2015-06-10 17:02:06 -04:00
|
|
|
dropletId := state.Get("droplet_id").(int)
|
2013-08-24 07:04:51 -04:00
|
|
|
|
2013-09-05 01:14:30 -04:00
|
|
|
// Gracefully power off the droplet. We have to retry this a number
|
|
|
|
// 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.
|
|
|
|
ui.Say("Gracefully shutting down droplet...")
|
2017-04-09 14:33:05 -04:00
|
|
|
_, _, err := client.DropletActions.Shutdown(context.TODO(), dropletId)
|
2013-08-24 07:04:51 -04:00
|
|
|
if err != nil {
|
2013-09-05 01:51:28 -04:00
|
|
|
// If we get an error the first time, actually report it
|
|
|
|
err := fmt.Errorf("Error shutting down droplet: %s", err)
|
2013-08-31 15:25:08 -04:00
|
|
|
state.Put("error", err)
|
2013-08-24 07:04:51 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2013-09-05 01:51:28 -04:00
|
|
|
// 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)
|
2017-04-09 14:38:19 -04:00
|
|
|
_, _, err := client.DropletActions.Shutdown(context.TODO(), dropletId)
|
2013-09-05 01:51:28 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Shutdown retry error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
return
|
|
|
|
case <-time.After(20 * time.Second):
|
|
|
|
// Retry!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2016-08-14 15:12:30 -04:00
|
|
|
err = waitForDropletState("off", dropletId, client, c.StateTimeout)
|
2013-09-05 01:51:28 -04:00
|
|
|
if err != nil {
|
2015-06-10 22:53:07 -04:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-08-14 15:12:30 -04:00
|
|
|
if err := waitForDropletUnlocked(client, dropletId, c.StateTimeout); err != nil {
|
2015-06-10 22:53:07 -04:00
|
|
|
// 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
|
2013-09-05 01:51:28 -04:00
|
|
|
}
|
|
|
|
|
2013-08-24 07:04:51 -04:00
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
2013-08-31 15:25:08 -04:00
|
|
|
func (s *stepShutdown) Cleanup(state multistep.StateBag) {
|
2013-08-24 07:04:51 -04:00
|
|
|
// no cleanup
|
|
|
|
}
|