diff --git a/builder/digitalocean/builder.go b/builder/digitalocean/builder.go index f2c841464..5cb4cd6a2 100644 --- a/builder/digitalocean/builder.go +++ b/builder/digitalocean/builder.go @@ -31,10 +31,12 @@ type config struct { SSHUsername string `mapstructure:"ssh_username"` SSHPort uint `mapstructure:"ssh_port"` SSHTimeout time.Duration + EventDelay time.Duration PackerDebug bool `mapstructure:"packer_debug"` RawSSHTimeout string `mapstructure:"ssh_timeout"` + RawEventDelay string `mapstructure:"event_delay"` } type Builder struct { @@ -88,6 +90,12 @@ func (b *Builder) Prepare(raws ...interface{}) error { b.config.RawSSHTimeout = "1m" } + if b.config.RawEventDelay == "" { + // Default to 5 second delays after creating events + // to allow DO to process + b.config.RawEventDelay = "5s" + } + // A list of errors on the configuration errs := make([]error, 0) @@ -100,12 +108,19 @@ func (b *Builder) Prepare(raws ...interface{}) error { if b.config.APIKey == "" { errs = append(errs, errors.New("an api_key must be specified")) } + timeout, err := time.ParseDuration(b.config.RawSSHTimeout) if err != nil { errs = append(errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err)) } b.config.SSHTimeout = timeout + delay, err := time.ParseDuration(b.config.RawEventDelay) + if err != nil { + errs = append(errs, fmt.Errorf("Failed parsing event_delay: %s", err)) + } + b.config.EventDelay = delay + if len(errs) > 0 { return &packer.MultiError{errs} } diff --git a/builder/digitalocean/builder_test.go b/builder/digitalocean/builder_test.go index 627779bee..aba0bb1b7 100644 --- a/builder/digitalocean/builder_test.go +++ b/builder/digitalocean/builder_test.go @@ -220,6 +220,38 @@ func TestBuilderPrepare_SSHTimeout(t *testing.T) { } +func TestBuilderPrepare_EventDelay(t *testing.T) { + var b Builder + config := testConfig() + + // Test default + err := b.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if b.config.RawEventDelay != "5s" { + t.Errorf("invalid: %d", b.config.RawEventDelay) + } + + // Test set + config["event_delay"] = "10s" + b = Builder{} + err = b.Prepare(config) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Test bad + config["event_delay"] = "tubes" + b = Builder{} + err = b.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + +} + func TestBuilderPrepare_SnapshotName(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/digitalocean/step_create_droplet.go b/builder/digitalocean/step_create_droplet.go index 36a745de5..a3ffdf9b3 100644 --- a/builder/digitalocean/step_create_droplet.go +++ b/builder/digitalocean/step_create_droplet.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" "time" ) @@ -49,6 +50,7 @@ func (s *stepCreateDroplet) Cleanup(state map[string]interface{}) { client := state["client"].(*DigitalOceanClient) ui := state["ui"].(packer.Ui) + c := state["config"].(config) // Destroy the droplet we just created ui.Say("Destroying droplet...") @@ -56,7 +58,8 @@ func (s *stepCreateDroplet) Cleanup(state map[string]interface{}) { // Sleep arbitrarily before sending destroy request // Otherwise we get "pending event" errors, even though there isn't // one. - time.Sleep(5 * time.Second) + log.Printf("Sleeping for %v, event_delay", c.RawEventDelay) + time.Sleep(c.EventDelay) err := client.DestroyDroplet(s.dropletId) diff --git a/builder/digitalocean/step_power_off.go b/builder/digitalocean/step_power_off.go index b487f7779..87bab6bb1 100644 --- a/builder/digitalocean/step_power_off.go +++ b/builder/digitalocean/step_power_off.go @@ -3,6 +3,7 @@ package digitalocean import ( "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "log" "time" ) @@ -10,13 +11,15 @@ type stepPowerOff struct{} func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction { client := state["client"].(*DigitalOceanClient) + c := state["config"].(config) ui := state["ui"].(packer.Ui) dropletId := state["droplet_id"].(uint) // Sleep arbitrarily before sending power off request // Otherwise we get "pending event" errors, even though there isn't // one. - time.Sleep(3 * time.Second) + log.Printf("Sleeping for %v, event_delay", c.RawEventDelay) + time.Sleep(c.EventDelay) // Poweroff the droplet so it can be snapshot err := client.PowerOffDroplet(dropletId)