diff --git a/builder/linode/builder_test.go b/builder/linode/builder_test.go index 8176114a4..d3e776ff0 100644 --- a/builder/linode/builder_test.go +++ b/builder/linode/builder_test.go @@ -3,6 +3,7 @@ package linode import ( "strconv" "testing" + "time" "github.com/hashicorp/packer/packer" ) @@ -248,3 +249,43 @@ func TestBuilderPrepare_Label(t *testing.T) { } } + +func TestBuilderPrepare_StateTimeout(t *testing.T) { + var b Builder + config := testConfig() + + // Test default + _, warnings, err := b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if b.config.StateTimeout != 5*time.Minute { + t.Errorf("invalid: %s", b.config.StateTimeout) + } + + // Test set + config["state_timeout"] = "5m" + b = Builder{} + _, warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Test bad + config["state_timeout"] = "tubes" + b = Builder{} + _, warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err == nil { + t.Fatal("should have error") + } +} diff --git a/builder/linode/config.go b/builder/linode/config.go index 548be2bc7..44a1a9d2b 100644 --- a/builder/linode/config.go +++ b/builder/linode/config.go @@ -9,6 +9,7 @@ import ( "fmt" "os" "regexp" + "time" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -24,16 +25,17 @@ type Config struct { PersonalAccessToken string `mapstructure:"linode_token"` - Region string `mapstructure:"region"` - InstanceType string `mapstructure:"instance_type"` - Label string `mapstructure:"instance_label"` - Tags []string `mapstructure:"instance_tags"` - Image string `mapstructure:"image"` - SwapSize int `mapstructure:"swap_size"` - RootPass string `mapstructure:"root_pass"` - RootSSHKey string `mapstructure:"root_ssh_key"` - ImageLabel string `mapstructure:"image_label"` - Description string `mapstructure:"image_description"` + Region string `mapstructure:"region"` + InstanceType string `mapstructure:"instance_type"` + Label string `mapstructure:"instance_label"` + Tags []string `mapstructure:"instance_tags"` + Image string `mapstructure:"image"` + SwapSize int `mapstructure:"swap_size"` + RootPass string `mapstructure:"root_pass"` + RootSSHKey string `mapstructure:"root_ssh_key"` + ImageLabel string `mapstructure:"image_label"` + Description string `mapstructure:"image_description"` + StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"` } func createRandomRootPassword() (string, error) { @@ -94,6 +96,11 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { } } + if c.StateTimeout == 0 { + // Default to 5 minute timeouts waiting for state change + c.StateTimeout = 5 * time.Minute + } + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } diff --git a/builder/linode/config.hcl2spec.go b/builder/linode/config.hcl2spec.go index 36c50d080..1b126735c 100644 --- a/builder/linode/config.hcl2spec.go +++ b/builder/linode/config.hcl2spec.go @@ -74,6 +74,7 @@ type FlatConfig struct { RootSSHKey *string `mapstructure:"root_ssh_key" cty:"root_ssh_key" hcl:"root_ssh_key"` ImageLabel *string `mapstructure:"image_label" cty:"image_label" hcl:"image_label"` Description *string `mapstructure:"image_description" cty:"image_description" hcl:"image_description"` + StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` } // FlatMapstructure returns a new FlatConfig. @@ -153,6 +154,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "root_ssh_key": &hcldec.AttrSpec{Name: "root_ssh_key", Type: cty.String, Required: false}, "image_label": &hcldec.AttrSpec{Name: "image_label", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, + "state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false}, } return s } diff --git a/builder/linode/step_shutdown_linode.go b/builder/linode/step_shutdown_linode.go index f9c065224..30ca39c57 100644 --- a/builder/linode/step_shutdown_linode.go +++ b/builder/linode/step_shutdown_linode.go @@ -14,6 +14,7 @@ type stepShutdownLinode struct { } func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + c := state.Get("config").(*Config) ui := state.Get("ui").(packer.Ui) instance := state.Get("instance").(*linodego.Instance) @@ -25,7 +26,7 @@ func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) return multistep.ActionHalt } - _, err := s.client.WaitForInstanceStatus(ctx, instance.ID, linodego.InstanceOffline, 120) + _, err := s.client.WaitForInstanceStatus(ctx, instance.ID, linodego.InstanceOffline, int(c.StateTimeout.Seconds())) if err != nil { err = errors.New("Error shutting down Linode: " + err.Error()) state.Put("error", err)