Commit ba8fbc86 authored by Jack Pearkes's avatar Jack Pearkes

builder/digitalocean: add configurable state_timeout

The state_timeout config allows you to determine the timeout
for "waiting for droplet to become [active, off, etc.]".

This still defaults to 3 minutes.
parent 1da55ab4
...@@ -39,12 +39,14 @@ type config struct { ...@@ -39,12 +39,14 @@ type config struct {
SSHPort uint `mapstructure:"ssh_port"` SSHPort uint `mapstructure:"ssh_port"`
SSHTimeout time.Duration SSHTimeout time.Duration
EventDelay time.Duration EventDelay time.Duration
StateTimeout time.Duration
PackerDebug bool `mapstructure:"packer_debug"` PackerDebug bool `mapstructure:"packer_debug"`
RawSnapshotName string `mapstructure:"snapshot_name"` RawSnapshotName string `mapstructure:"snapshot_name"`
RawSSHTimeout string `mapstructure:"ssh_timeout"` RawSSHTimeout string `mapstructure:"ssh_timeout"`
RawEventDelay string `mapstructure:"event_delay"` RawEventDelay string `mapstructure:"event_delay"`
RawStateTimeout string `mapstructure:"state_timeout"`
} }
type Builder struct { type Builder struct {
...@@ -104,6 +106,12 @@ func (b *Builder) Prepare(raws ...interface{}) error { ...@@ -104,6 +106,12 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.RawEventDelay = "5s" b.config.RawEventDelay = "5s"
} }
if b.config.RawStateTimeout == "" {
// Default to 3 minute timeouts waiting for
// desired state. i.e waiting for droplet to become active
b.config.RawStateTimeout = "3m"
}
// A list of errors on the configuration // A list of errors on the configuration
errs := make([]error, 0) errs := make([]error, 0)
...@@ -117,17 +125,23 @@ func (b *Builder) Prepare(raws ...interface{}) error { ...@@ -117,17 +125,23 @@ func (b *Builder) Prepare(raws ...interface{}) error {
errs = append(errs, errors.New("an api_key must be specified")) errs = append(errs, errors.New("an api_key must be specified"))
} }
timeout, err := time.ParseDuration(b.config.RawSSHTimeout) sshTimeout, err := time.ParseDuration(b.config.RawSSHTimeout)
if err != nil { if err != nil {
errs = append(errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err)) errs = append(errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
} }
b.config.SSHTimeout = timeout b.config.SSHTimeout = sshTimeout
delay, err := time.ParseDuration(b.config.RawEventDelay) eventDelay, err := time.ParseDuration(b.config.RawEventDelay)
if err != nil { if err != nil {
errs = append(errs, fmt.Errorf("Failed parsing event_delay: %s", err)) errs = append(errs, fmt.Errorf("Failed parsing event_delay: %s", err))
} }
b.config.EventDelay = delay b.config.EventDelay = eventDelay
stateTimeout, err := time.ParseDuration(b.config.RawStateTimeout)
if err != nil {
errs = append(errs, fmt.Errorf("Failed parsing state_timeout: %s", err))
}
b.config.StateTimeout = stateTimeout
// Parse the name of the snapshot // Parse the name of the snapshot
snapNameBuf := new(bytes.Buffer) snapNameBuf := new(bytes.Buffer)
......
...@@ -253,6 +253,38 @@ func TestBuilderPrepare_EventDelay(t *testing.T) { ...@@ -253,6 +253,38 @@ func TestBuilderPrepare_EventDelay(t *testing.T) {
} }
func TestBuilderPrepare_StateTimeout(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.RawStateTimeout != "3m" {
t.Errorf("invalid: %d", b.config.RawStateTimeout)
}
// Test set
config["state_timeout"] = "5m"
b = Builder{}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["state_timeout"] = "tubes"
b = Builder{}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_SnapshotName(t *testing.T) { func TestBuilderPrepare_SnapshotName(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()
......
...@@ -11,11 +11,12 @@ type stepDropletInfo struct{} ...@@ -11,11 +11,12 @@ type stepDropletInfo struct{}
func (s *stepDropletInfo) Run(state map[string]interface{}) multistep.StepAction { func (s *stepDropletInfo) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient) client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui) ui := state["ui"].(packer.Ui)
c := state["config"].(config)
dropletId := state["droplet_id"].(uint) dropletId := state["droplet_id"].(uint)
ui.Say("Waiting for droplet to become active...") ui.Say("Waiting for droplet to become active...")
err := waitForDropletState("active", dropletId, client) err := waitForDropletState("active", dropletId, client, c)
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["error"] = err state["error"] = err
......
...@@ -34,7 +34,7 @@ func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction { ...@@ -34,7 +34,7 @@ func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction {
ui.Say("Waiting for droplet to power off...") ui.Say("Waiting for droplet to power off...")
err = waitForDropletState("off", dropletId, client) 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["error"] = err state["error"] = err
......
...@@ -26,7 +26,7 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction { ...@@ -26,7 +26,7 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
} }
ui.Say("Waiting for snapshot to complete...") ui.Say("Waiting for snapshot to complete...")
err = waitForDropletState("active", dropletId, client) err = waitForDropletState("active", dropletId, client, c)
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["error"] = err state["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) error { func waitForDropletState(desiredState string, dropletId uint, client *DigitalOceanClient, c config) error {
active := make(chan bool, 1) active := make(chan bool, 1)
go func() { go func() {
...@@ -36,9 +36,8 @@ func waitForDropletState(desiredState string, dropletId uint, client *DigitalOce ...@@ -36,9 +36,8 @@ func waitForDropletState(desiredState string, dropletId uint, client *DigitalOce
active <- true active <- true
}() }()
log.Printf("Waiting for up to 3 minutes for droplet to become %s", desiredState) log.Printf("Waiting for up to %s for droplet to become %s", c.RawStateTimeout, desiredState)
duration, _ := time.ParseDuration("3m") timeout := time.After(c.StateTimeout)
timeout := time.After(duration)
ActiveWaitLoop: ActiveWaitLoop:
for { for {
......
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