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.
This commit is contained in:
parent
1da55ab4b6
commit
ba8fbc8621
|
@ -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 {
|
||||||
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 {
|
||||||
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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{}
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
||||||
|
|
||||||
// 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
|
||||||
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 {
|
||||||
|
|
Loading…
Reference in New Issue