diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 1cd6a2073..5aad1e633 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -398,12 +398,15 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack GeneratedData: generatedData, }, &StepCreateVolume{ + PollingConfig: b.config.PollingConfig, RootVolumeType: b.config.RootVolumeType, RootVolumeSize: b.config.RootVolumeSize, RootVolumeTags: b.config.RootVolumeTags, Ctx: b.config.ctx, }, - &StepAttachVolume{}, + &StepAttachVolume{ + PollingConfig: b.config.PollingConfig, + }, &StepEarlyUnflock{}, &chroot.StepPreMountCommands{ Commands: b.config.PreMountCommands, @@ -427,7 +430,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &chroot.StepChrootProvision{}, &chroot.StepEarlyCleanup{}, - &StepSnapshot{}, + &StepSnapshot{ + PollingConfig: b.config.PollingConfig, + }, &awscommon.StepDeregisterAMI{ AccessConfig: &b.config.AccessConfig, ForceDeregister: b.config.AMIForceDeregister, @@ -440,6 +445,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack EnableAMISriovNetSupport: b.config.AMISriovNetSupport, EnableAMIENASupport: b.config.AMIENASupport, AMISkipBuildRegion: b.config.AMISkipBuildRegion, + PollingConfig: b.config.PollingConfig, }, &awscommon.StepAMIRegionCopy{ AccessConfig: &b.config.AccessConfig, diff --git a/builder/amazon/chroot/builder.hcl2spec.go b/builder/amazon/chroot/builder.hcl2spec.go index 977c33df6..9f87fb150 100644 --- a/builder/amazon/chroot/builder.hcl2spec.go +++ b/builder/amazon/chroot/builder.hcl2spec.go @@ -52,6 +52,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" hcl2-schema-generator:"ami_block_device_mappings,direct" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"` ChrootMounts [][]string `mapstructure:"chroot_mounts" required:"false" cty:"chroot_mounts" hcl:"chroot_mounts"` CommandWrapper *string `mapstructure:"command_wrapper" required:"false" cty:"command_wrapper" hcl:"command_wrapper"` @@ -127,6 +128,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "chroot_mounts": &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.List(cty.String)), Required: false}, "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, diff --git a/builder/amazon/chroot/step_attach_volume.go b/builder/amazon/chroot/step_attach_volume.go index 7044e0218..651a2ff8a 100644 --- a/builder/amazon/chroot/step_attach_volume.go +++ b/builder/amazon/chroot/step_attach_volume.go @@ -19,8 +19,9 @@ import ( // device string - The location where the volume was attached. // attach_cleanup CleanupFunc type StepAttachVolume struct { - attached bool - volumeId string + PollingConfig *awscommon.AWSPollingConfig + attached bool + volumeId string } func (s *StepAttachVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -51,7 +52,7 @@ func (s *StepAttachVolume) Run(ctx context.Context, state multistep.StateBag) mu s.volumeId = volumeId // Wait for the volume to become attached - err = awscommon.WaitUntilVolumeAttached(ctx, ec2conn, s.volumeId) + err = s.PollingConfig.WaitUntilVolumeAttached(ctx, ec2conn, s.volumeId) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state.Put("error", err) @@ -87,7 +88,7 @@ func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error { s.attached = false // Wait for the volume to detach - err = awscommon.WaitUntilVolumeDetached(aws.BackgroundContext(), ec2conn, s.volumeId) + err = s.PollingConfig.WaitUntilVolumeDetached(aws.BackgroundContext(), ec2conn, s.volumeId) if err != nil { return fmt.Errorf("Error waiting for volume: %s", err) } diff --git a/builder/amazon/chroot/step_create_volume.go b/builder/amazon/chroot/step_create_volume.go index 64758e9cd..7b01eced3 100644 --- a/builder/amazon/chroot/step_create_volume.go +++ b/builder/amazon/chroot/step_create_volume.go @@ -20,6 +20,7 @@ import ( // Produces: // volume_id string - The ID of the created volume type StepCreateVolume struct { + PollingConfig *awscommon.AWSPollingConfig volumeId string RootVolumeSize int64 RootVolumeType string @@ -110,7 +111,7 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu log.Printf("Volume ID: %s", s.volumeId) // Wait for the volume to become ready - err = awscommon.WaitUntilVolumeAvailable(ctx, ec2conn, s.volumeId) + err = s.PollingConfig.WaitUntilVolumeAvailable(ctx, ec2conn, s.volumeId) if err != nil { err := fmt.Errorf("Error waiting for volume: %s", err) state.Put("error", err) diff --git a/builder/amazon/chroot/step_register_ami.go b/builder/amazon/chroot/step_register_ami.go index e38ad83ee..f7e85dc07 100644 --- a/builder/amazon/chroot/step_register_ami.go +++ b/builder/amazon/chroot/step_register_ami.go @@ -15,6 +15,7 @@ import ( // StepRegisterAMI creates the AMI. type StepRegisterAMI struct { + PollingConfig *awscommon.AWSPollingConfig RootVolumeSize int64 EnableAMIENASupport confighelper.Trilean EnableAMISriovNetSupport bool @@ -81,7 +82,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul state.Put("amis", amis) ui.Say("Waiting for AMI to become ready...") - if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { + if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/amazon/chroot/step_snapshot.go b/builder/amazon/chroot/step_snapshot.go index 4c66b4fd1..408c1f0c5 100644 --- a/builder/amazon/chroot/step_snapshot.go +++ b/builder/amazon/chroot/step_snapshot.go @@ -16,7 +16,8 @@ import ( // Produces: // snapshot_id string - ID of the created snapshot type StepSnapshot struct { - snapshotId string + PollingConfig *awscommon.AWSPollingConfig + snapshotId string } func (s *StepSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -43,7 +44,7 @@ func (s *StepSnapshot) Run(ctx context.Context, state multistep.StateBag) multis ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId)) // Wait for the snapshot to be ready - err = awscommon.WaitUntilSnapshotDone(ctx, ec2conn, s.snapshotId) + err = s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, s.snapshotId) if err != nil { err := fmt.Errorf("Error waiting for snapshot: %s", err) state.Put("error", err) diff --git a/builder/amazon/common/access_config.go b/builder/amazon/common/access_config.go index 6ce2e1438..9b645ada3 100644 --- a/builder/amazon/common/access_config.go +++ b/builder/amazon/common/access_config.go @@ -138,6 +138,9 @@ type AccessConfig struct { // } // ``` VaultAWSEngine VaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false"` + // [Polling configuration](#polling-configuration) for the AWS waiter. Configures the waiter that checks + // resource state. + PollingConfig *AWSPollingConfig `mapstructure:"aws_polling" required:"false"` getEC2Connection func() ec2iface.EC2API } @@ -215,7 +218,6 @@ func (c *AccessConfig) Session() (*session.Session, error) { if c.DecodeAuthZMessages { DecodeAuthZMessages(c.session) } - LogEnvOverrideWarnings() return c.session, nil } @@ -296,6 +298,11 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { fmt.Errorf("`access_key` and `secret_key` must both be either set or not set.")) } + if c.PollingConfig == nil { + c.PollingConfig = new(AWSPollingConfig) + } + c.PollingConfig.LogEnvOverrideWarnings() + return errs } diff --git a/builder/amazon/common/access_config_test.go b/builder/amazon/common/access_config_test.go index 489bf08df..57e5fd92c 100644 --- a/builder/amazon/common/access_config_test.go +++ b/builder/amazon/common/access_config_test.go @@ -13,6 +13,7 @@ func testAccessConfig() *AccessConfig { getEC2Connection: func() ec2iface.EC2API { return &mockEC2Client{} }, + PollingConfig: new(AWSPollingConfig), } } diff --git a/builder/amazon/common/state.go b/builder/amazon/common/state.go index 4e08d2733..113a36ab8 100644 --- a/builder/amazon/common/state.go +++ b/builder/amazon/common/state.go @@ -1,3 +1,5 @@ +//go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type AWSPollingConfig package common import ( @@ -38,12 +40,45 @@ type StateChangeConf struct { // Following are wrapper functions that use Packer's environment-variables to // determine retry logic, then call the AWS SDK's built-in waiters. -func WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.EC2API, imageId string) error { +// Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching +// volumes or importing image. +// Usage example: +// +// In JSON: +// ```json +// "aws_polling" : { +// "delay_seconds": 30, +// "max_attempts": 50 +// } +// ``` +// +// In HCL2: +// ```hcl +// aws_polling { +// delay_seconds = 30 +// max_attempts = 50 +// } +// ``` +// +type AWSPollingConfig struct { + // Specifies the maximum number of attempts the waiter will check for resource state. + // This value can also be set via the AWS_MAX_ATTEMPTS. + // If both option and environment variable are set, the max_attempts will be considered over the AWS_MAX_ATTEMPTS. + // If none is set, defaults to AWS waiter default which is 40 max_attempts. + MaxAttempts int `mapstructure:"max_attempts" required:"false"` + // Specifies the delay in seconds between attempts to check the resource state. + // This value can also be set via the AWS_POLL_DELAY_SECONDS. + // If both option and environment variable are set, the delay_seconds will be considered over the AWS_POLL_DELAY_SECONDS. + // If none is set, defaults to AWS waiter default which is 15 seconds. + DelaySeconds int `mapstructure:"delay_seconds" required:"false"` +} + +func (w *AWSPollingConfig) WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.EC2API, imageId string) error { imageInput := ec2.DescribeImagesInput{ ImageIds: []*string{&imageId}, } - waitOpts := getWaiterOptions() + waitOpts := w.getWaiterOptions() if len(waitOpts) == 0 { // Bump this default to 30 minutes because the aws default // of ten minutes doesn't work for some of our long-running copies. @@ -66,7 +101,7 @@ func WaitUntilAMIAvailable(ctx aws.Context, conn ec2iface.EC2API, imageId string return err } -func WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.EC2, instanceId string) error { +func (w *AWSPollingConfig) WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.EC2, instanceId string) error { instanceInput := ec2.DescribeInstancesInput{ InstanceIds: []*string{&instanceId}, @@ -75,12 +110,11 @@ func WaitUntilInstanceRunning(ctx aws.Context, conn *ec2.EC2, instanceId string) err := conn.WaitUntilInstanceRunningWithContext( ctx, &instanceInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } -func WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error { - +func (w *AWSPollingConfig) WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId string) error { instanceInput := ec2.DescribeInstancesInput{ InstanceIds: []*string{&instanceId}, } @@ -88,12 +122,12 @@ func WaitUntilInstanceTerminated(ctx aws.Context, conn *ec2.EC2, instanceId stri err := conn.WaitUntilInstanceTerminatedWithContext( ctx, &instanceInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } // This function works for both requesting and cancelling spot instances. -func WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error { +func (w *AWSPollingConfig) WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId string) error { spotRequestInput := ec2.DescribeSpotInstanceRequestsInput{ SpotInstanceRequestIds: []*string{&spotRequestId}, } @@ -101,11 +135,11 @@ func WaitUntilSpotRequestFulfilled(ctx aws.Context, conn *ec2.EC2, spotRequestId err := conn.WaitUntilSpotInstanceRequestFulfilledWithContext( ctx, &spotRequestInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } -func WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error { +func (w *AWSPollingConfig) WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) error { volumeInput := ec2.DescribeVolumesInput{ VolumeIds: []*string{&volumeId}, } @@ -113,11 +147,11 @@ func WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.EC2, volumeId string) e err := conn.WaitUntilVolumeAvailableWithContext( ctx, &volumeInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } -func WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error { +func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error { snapInput := ec2.DescribeSnapshotsInput{ SnapshotIds: []*string{&snapshotID}, } @@ -125,13 +159,13 @@ func WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) er err := conn.WaitUntilSnapshotCompletedWithContext( ctx, &snapInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } // Wrappers for our custom AWS waiters -func WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error { +func (w *AWSPollingConfig) WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) error { volumeInput := ec2.DescribeVolumesInput{ VolumeIds: []*string{&volumeId}, } @@ -139,11 +173,11 @@ func WaitUntilVolumeAttached(ctx aws.Context, conn *ec2.EC2, volumeId string) er err := WaitForVolumeToBeAttached(conn, ctx, &volumeInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } -func WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error { +func (w *AWSPollingConfig) WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) error { volumeInput := ec2.DescribeVolumesInput{ VolumeIds: []*string{&volumeId}, } @@ -151,11 +185,11 @@ func WaitUntilVolumeDetached(ctx aws.Context, conn *ec2.EC2, volumeId string) er err := WaitForVolumeToBeDetached(conn, ctx, &volumeInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } -func WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error { +func (w *AWSPollingConfig) WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error { importInput := ec2.DescribeImportImageTasksInput{ ImportTaskIds: []*string{&taskID}, } @@ -163,7 +197,7 @@ func WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2, taskID string) error err := WaitForImageToBeImported(conn, ctx, &importInput, - getWaiterOptions()...) + w.getWaiterOptions()...) return err } @@ -298,8 +332,18 @@ type overridableWaitVars struct { awsTimeoutSeconds envInfo } -func getWaiterOptions() []request.WaiterOption { +func (w *AWSPollingConfig) getWaiterOptions() []request.WaiterOption { envOverrides := getEnvOverrides() + + if w.MaxAttempts != 0 { + envOverrides.awsMaxAttempts.Val = w.MaxAttempts + envOverrides.awsMaxAttempts.overridden = true + } + if w.DelaySeconds != 0 { + envOverrides.awsPollDelaySeconds.Val = w.DelaySeconds + envOverrides.awsPollDelaySeconds.overridden = true + } + waitOpts := applyEnvOverrides(envOverrides) return waitOpts } @@ -333,33 +377,38 @@ func getEnvOverrides() overridableWaitVars { return envValues } -func LogEnvOverrideWarnings() { - pollDelay := os.Getenv("AWS_POLL_DELAY_SECONDS") - timeoutSeconds := os.Getenv("AWS_TIMEOUT_SECONDS") - maxAttempts := os.Getenv("AWS_MAX_ATTEMPTS") +func (w *AWSPollingConfig) LogEnvOverrideWarnings() { + pollDelayEnv := os.Getenv("AWS_POLL_DELAY_SECONDS") + timeoutSecondsEnv := os.Getenv("AWS_TIMEOUT_SECONDS") + maxAttemptsEnv := os.Getenv("AWS_MAX_ATTEMPTS") - if maxAttempts != "" && timeoutSeconds != "" { + maxAttemptsIsSet := maxAttemptsEnv != "" || w.MaxAttempts != 0 + timeoutSecondsIsSet := timeoutSecondsEnv != "" + pollDelayIsSet := pollDelayEnv != "" || w.DelaySeconds != 0 + + if maxAttemptsIsSet && timeoutSecondsIsSet { warning := fmt.Sprintf("[WARNING] (aws): AWS_MAX_ATTEMPTS and " + "AWS_TIMEOUT_SECONDS are both set. Packer will use " + "AWS_MAX_ATTEMPTS and discard AWS_TIMEOUT_SECONDS.") - if pollDelay == "" { + if !pollDelayIsSet { warning = fmt.Sprintf("%s Since you have not set the poll delay, "+ "Packer will default to a 2-second delay.", warning) } log.Printf(warning) - } else if timeoutSeconds != "" { + } else if timeoutSecondsIsSet { log.Printf("[WARNING] (aws): env var AWS_TIMEOUT_SECONDS is " + - "deprecated in favor of AWS_MAX_ATTEMPTS. If you have not " + - "explicitly set AWS_POLL_DELAY_SECONDS, we are defaulting to a " + - "poll delay of 2 seconds, regardless of the AWS waiter's default.") + "deprecated in favor of AWS_MAX_ATTEMPTS env or aws_polling_max_attempts config option. " + + "If you have not explicitly set AWS_POLL_DELAY_SECONDS env or aws_polling_delay_seconds config option, " + + "we are defaulting to a poll delay of 2 seconds, regardless of the AWS waiter's default.") } - if maxAttempts == "" && timeoutSeconds == "" && pollDelay == "" { + if !maxAttemptsIsSet && !timeoutSecondsIsSet && !pollDelayIsSet { log.Printf("[INFO] (aws): No AWS timeout and polling overrides have been set. " + "Packer will default to waiter-specific delays and timeouts. If you would " + "like to customize the length of time between retries and max " + "number of retries you may do so by setting the environment " + - "variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS to your " + - "desired values.") + "variables AWS_POLL_DELAY_SECONDS and AWS_MAX_ATTEMPTS or the " + + "configuration options aws_polling_delay_seconds and aws_polling_max_attempts " + + "to your desired values.") } } diff --git a/builder/amazon/common/state.hcl2spec.go b/builder/amazon/common/state.hcl2spec.go new file mode 100644 index 000000000..e3c240f89 --- /dev/null +++ b/builder/amazon/common/state.hcl2spec.go @@ -0,0 +1,32 @@ +// Code generated by "mapstructure-to-hcl2 -type AWSPollingConfig"; DO NOT EDIT. +package common + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatAWSPollingConfig is an auto-generated flat version of AWSPollingConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatAWSPollingConfig struct { + MaxAttempts *int `mapstructure:"max_attempts" required:"false" cty:"max_attempts" hcl:"max_attempts"` + DelaySeconds *int `mapstructure:"delay_seconds" required:"false" cty:"delay_seconds" hcl:"delay_seconds"` +} + +// FlatMapstructure returns a new FlatAWSPollingConfig. +// FlatAWSPollingConfig is an auto-generated flat version of AWSPollingConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*AWSPollingConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAWSPollingConfig) +} + +// HCL2Spec returns the hcl spec of a AWSPollingConfig. +// This spec is used by HCL to read the fields of AWSPollingConfig. +// The decoded values from this spec will then be applied to a FlatAWSPollingConfig. +func (*FlatAWSPollingConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "max_attempts": &hcldec.AttrSpec{Name: "max_attempts", Type: cty.Number, Required: false}, + "delay_seconds": &hcldec.AttrSpec{Name: "delay_seconds", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go index f30b8f415..c38db6771 100644 --- a/builder/amazon/common/step_ami_region_copy.go +++ b/builder/amazon/common/step_ami_region_copy.go @@ -209,7 +209,7 @@ func (s *StepAMIRegionCopy) amiRegionCopy(ctx context.Context, state multistep.S } // Wait for the image to become ready - if err := WaitUntilAMIAvailable(ctx, regionconn, *resp.ImageId); err != nil { + if err := s.AccessConfig.PollingConfig.WaitUntilAMIAvailable(ctx, regionconn, *resp.ImageId); err != nil { return "", snapshotIds, fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", *resp.ImageId, target, err) } diff --git a/builder/amazon/common/step_run_source_instance.go b/builder/amazon/common/step_run_source_instance.go index 34f0c076a..76939768e 100644 --- a/builder/amazon/common/step_run_source_instance.go +++ b/builder/amazon/common/step_run_source_instance.go @@ -19,6 +19,7 @@ import ( ) type StepRunSourceInstance struct { + PollingConfig *AWSPollingConfig AssociatePublicIpAddress bool LaunchMappings EC2BlockDeviceMappingsBuilder Comm *communicator.Config @@ -234,7 +235,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa InstanceIds: []*string{aws.String(instanceId)}, } - if err := WaitUntilInstanceRunning(ctx, ec2conn, instanceId); err != nil { + if err := s.PollingConfig.WaitUntilInstanceRunning(ctx, ec2conn, instanceId); err != nil { err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", instanceId, err) state.Put("error", err) ui.Error(err.Error()) @@ -364,7 +365,7 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) { return } - if err := WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil { + if err := s.PollingConfig.WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil { ui.Error(err.Error()) } } diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index 6ae97985e..f1b484b81 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -23,6 +23,7 @@ type EC2BlockDeviceMappingsBuilder interface { } type StepRunSpotInstance struct { + PollingConfig *AWSPollingConfig AssociatePublicIpAddress bool LaunchMappings EC2BlockDeviceMappingsBuilder BlockDurationMinutes int64 @@ -466,7 +467,7 @@ func (s *StepRunSpotInstance) Cleanup(state multistep.StateBag) { return } - if err := WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil { + if err := s.PollingConfig.WaitUntilInstanceTerminated(aws.BackgroundContext(), ec2conn, s.instanceId); err != nil { ui.Error(err.Error()) } } diff --git a/builder/amazon/common/step_run_spot_instance_test.go b/builder/amazon/common/step_run_spot_instance_test.go index 7ecef5d3a..89fa53f01 100644 --- a/builder/amazon/common/step_run_spot_instance_test.go +++ b/builder/amazon/common/step_run_spot_instance_test.go @@ -29,6 +29,7 @@ func tStateSpot() multistep.StateBag { func getBasicStep() *StepRunSpotInstance { stepRunSpotInstance := StepRunSpotInstance{ + PollingConfig: new(AWSPollingConfig), AssociatePublicIpAddress: false, LaunchMappings: BlockDevices{}, BlockDurationMinutes: 0, diff --git a/builder/amazon/common/step_stop_ebs_instance.go b/builder/amazon/common/step_stop_ebs_instance.go index 794f1c6bb..f02f15bfc 100644 --- a/builder/amazon/common/step_stop_ebs_instance.go +++ b/builder/amazon/common/step_stop_ebs_instance.go @@ -12,6 +12,7 @@ import ( ) type StepStopEBSBackedInstance struct { + PollingConfig *AWSPollingConfig Skip bool DisableStopInstance bool } @@ -74,7 +75,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta &ec2.DescribeInstancesInput{ InstanceIds: []*string{instance.InstanceId}, }, - getWaiterOptions()...) + s.PollingConfig.getWaiterOptions()...) if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 5243df8d0..a19fbc423 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -169,6 +169,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, BlockDurationMinutes: b.config.BlockDurationMinutes, @@ -191,6 +192,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } } else { instanceStep = &awscommon.StepRunSourceInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, Comm: &b.config.RunConfig.Comm, @@ -292,6 +294,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Comm: &b.config.RunConfig.Comm, }, &awscommon.StepStopEBSBackedInstance{ + PollingConfig: b.config.PollingConfig, Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, @@ -308,6 +311,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &stepCreateAMI{ AMISkipBuildRegion: b.config.AMISkipBuildRegion, + PollingConfig: b.config.PollingConfig, }, &awscommon.StepAMIRegionCopy{ AccessConfig: &b.config.AccessConfig, diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index da67ed57b..0e3bcac1c 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -31,6 +31,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name" hcl:"ami_name"` AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description" hcl:"ami_description"` AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type" hcl:"ami_virtualization_type"` @@ -171,6 +172,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, diff --git a/builder/amazon/ebs/step_create_ami.go b/builder/amazon/ebs/step_create_ami.go index 1f60c267a..74a3a419a 100644 --- a/builder/amazon/ebs/step_create_ami.go +++ b/builder/amazon/ebs/step_create_ami.go @@ -16,6 +16,7 @@ import ( ) type stepCreateAMI struct { + PollingConfig *awscommon.AWSPollingConfig image *ec2.Image AMISkipBuildRegion bool } @@ -85,8 +86,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi // Wait for the image to become ready ui.Say("Waiting for AMI to become ready...") - - if waitErr := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *createResp.ImageId); waitErr != nil { + if waitErr := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *createResp.ImageId); waitErr != nil { // waitErr should get bubbled up if the issue is a wait timeout err := fmt.Errorf("Error waiting for AMI: %s", waitErr) imResp, imerr := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{createResp.ImageId}}) diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 9a043dcb7..f53124128 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -192,6 +192,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, BlockDurationMinutes: b.config.BlockDurationMinutes, @@ -213,6 +214,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } } else { instanceStep = &awscommon.StepRunSourceInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, Comm: &b.config.RunConfig.Comm, @@ -316,6 +318,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Comm: &b.config.RunConfig.Comm, }, &awscommon.StepStopEBSBackedInstance{ + PollingConfig: b.config.PollingConfig, Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, @@ -325,6 +328,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack EnableAMIENASupport: b.config.AMIENASupport, }, &StepSnapshotVolumes{ + PollingConfig: b.config.PollingConfig, LaunchDevices: launchDevices, SnapshotOmitMap: b.config.LaunchMappings.GetOmissions(), }, @@ -344,6 +348,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Architecture: b.config.Architecture, LaunchOmitMap: b.config.LaunchMappings.GetOmissions(), AMISkipBuildRegion: b.config.AMISkipBuildRegion, + PollingConfig: b.config.PollingConfig, }, &awscommon.StepAMIRegionCopy{ AccessConfig: &b.config.AccessConfig, diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go index 5c839e89c..733a8f2e5 100644 --- a/builder/amazon/ebssurrogate/builder.hcl2spec.go +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -74,6 +74,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"` AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"` BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes" hcl:"block_duration_minutes"` @@ -215,6 +216,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, diff --git a/builder/amazon/ebssurrogate/step_register_ami.go b/builder/amazon/ebssurrogate/step_register_ami.go index 1a6760089..7d6f05fbf 100644 --- a/builder/amazon/ebssurrogate/step_register_ami.go +++ b/builder/amazon/ebssurrogate/step_register_ami.go @@ -15,6 +15,7 @@ import ( // StepRegisterAMI creates the AMI. type StepRegisterAMI struct { + PollingConfig *awscommon.AWSPollingConfig RootDevice RootBlockDevice AMIDevices []*ec2.BlockDeviceMapping LaunchDevices []*ec2.BlockDeviceMapping @@ -86,7 +87,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul // Wait for the image to become ready ui.Say("Waiting for AMI to become ready...") - if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { + if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/amazon/ebssurrogate/step_register_ami_test.go b/builder/amazon/ebssurrogate/step_register_ami_test.go index d2ef67685..1f4ac0752 100644 --- a/builder/amazon/ebssurrogate/step_register_ami_test.go +++ b/builder/amazon/ebssurrogate/step_register_ami_test.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/packer/builder/amazon/common" ) const sourceDeviceName = "/dev/xvdf" @@ -23,6 +24,7 @@ func newStepRegisterAMI(amiDevices, launchDevices []*ec2.BlockDeviceMapping) *St }, AMIDevices: amiDevices, LaunchDevices: launchDevices, + PollingConfig: new(common.AWSPollingConfig), } } diff --git a/builder/amazon/ebssurrogate/step_snapshot_volumes.go b/builder/amazon/ebssurrogate/step_snapshot_volumes.go index a88aac1a5..60cb393f2 100644 --- a/builder/amazon/ebssurrogate/step_snapshot_volumes.go +++ b/builder/amazon/ebssurrogate/step_snapshot_volumes.go @@ -18,6 +18,7 @@ import ( // Produces: // snapshot_ids map[string]string - IDs of the created snapshots type StepSnapshotVolumes struct { + PollingConfig *awscommon.AWSPollingConfig LaunchDevices []*ec2.BlockDeviceMapping snapshotIds map[string]string snapshotMutex sync.Mutex @@ -56,7 +57,7 @@ func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName str s.snapshotMutex.Unlock() // Wait for snapshot to be created - err = awscommon.WaitUntilSnapshotDone(ctx, ec2conn, *createSnapResp.SnapshotId) + err = s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, *createSnapResp.SnapshotId) return err } diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index 07ef24878..f6aa47b2f 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -173,6 +173,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.launchBlockDevices, BlockDurationMinutes: b.config.BlockDurationMinutes, @@ -194,6 +195,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } } else { instanceStep = &awscommon.StepRunSourceInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.launchBlockDevices, Comm: &b.config.RunConfig.Comm, @@ -286,6 +288,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Comm: &b.config.RunConfig.Comm, }, &awscommon.StepStopEBSBackedInstance{ + PollingConfig: b.config.PollingConfig, Skip: b.config.IsSpotInstance(), DisableStopInstance: b.config.DisableStopInstance, }, diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go index 3d0b80736..392bdf304 100644 --- a/builder/amazon/ebsvolume/builder.hcl2spec.go +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -76,6 +76,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"` AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"` BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes" hcl:"block_duration_minutes"` @@ -195,6 +196,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 9e254c76c..b408ecf7d 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -255,6 +255,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.IsSpotInstance() { instanceStep = &awscommon.StepRunSpotInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, BlockDurationMinutes: b.config.BlockDurationMinutes, @@ -273,6 +274,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } } else { instanceStep = &awscommon.StepRunSourceInstance{ + PollingConfig: b.config.PollingConfig, AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, LaunchMappings: b.config.LaunchMappings, Comm: &b.config.RunConfig.Comm, @@ -385,6 +387,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack EnableAMISriovNetSupport: b.config.AMISriovNetSupport, EnableAMIENASupport: b.config.AMIENASupport, AMISkipBuildRegion: b.config.AMISkipBuildRegion, + PollingConfig: b.config.PollingConfig, }, &awscommon.StepAMIRegionCopy{ AccessConfig: &b.config.AccessConfig, diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go index e3a7278a3..6abbc5032 100644 --- a/builder/amazon/instance/builder.hcl2spec.go +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -31,6 +31,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name" hcl:"ami_name"` AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description" hcl:"ami_description"` AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type" hcl:"ami_virtualization_type"` @@ -177,6 +178,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, diff --git a/builder/amazon/instance/step_register_ami.go b/builder/amazon/instance/step_register_ami.go index 9abee7a51..3aba13dc2 100644 --- a/builder/amazon/instance/step_register_ami.go +++ b/builder/amazon/instance/step_register_ami.go @@ -14,6 +14,7 @@ import ( ) type StepRegisterAMI struct { + PollingConfig *awscommon.AWSPollingConfig EnableAMIENASupport confighelper.Trilean EnableAMISriovNetSupport bool AMISkipBuildRegion bool @@ -80,7 +81,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul // Wait for the image to become ready ui.Say("Waiting for AMI to become ready...") - if err := awscommon.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { + if err := s.PollingConfig.WaitUntilAMIAvailable(ctx, ec2conn, *registerResp.ImageId); err != nil { err := fmt.Errorf("Error waiting for AMI: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/post-processor/amazon-import/post-processor.go b/post-processor/amazon-import/post-processor.go index f6cd99d2b..12179b918 100644 --- a/post-processor/amazon-import/post-processor.go +++ b/post-processor/amazon-import/post-processor.go @@ -119,7 +119,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { if len(errs.Errors) > 0 { return errs } - awscommon.LogEnvOverrideWarnings() + if p.config.PollingConfig == nil { + p.config.PollingConfig = new(awscommon.AWSPollingConfig) + } + p.config.PollingConfig.LogEnvOverrideWarnings() packer.LogSecretFilter.Set(p.config.AccessKey, p.config.SecretKey, p.config.Token) log.Println(p.config) @@ -250,7 +253,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact // Wait for import process to complete, this takes a while ui.Message(fmt.Sprintf("Waiting for task %s to complete (may take a while)", *import_start.ImportTaskId)) - err = awscommon.WaitUntilImageImported(aws.BackgroundContext(), ec2conn, *import_start.ImportTaskId) + err = p.config.PollingConfig.WaitUntilImageImported(aws.BackgroundContext(), ec2conn, *import_start.ImportTaskId) if err != nil { // Retrieve the status message @@ -313,7 +316,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact ui.Message(fmt.Sprintf("Waiting for AMI rename to complete (may take a while)")) - if err := awscommon.WaitUntilAMIAvailable(aws.BackgroundContext(), ec2conn, *resp.ImageId); err != nil { + if err := p.config.PollingConfig.WaitUntilAMIAvailable(aws.BackgroundContext(), ec2conn, *resp.ImageId); err != nil { return nil, false, false, fmt.Errorf("Error waiting for AMI (%s): %s", *resp.ImageId, err) } diff --git a/post-processor/amazon-import/post-processor.hcl2spec.go b/post-processor/amazon-import/post-processor.hcl2spec.go index 17a6ee843..b35ae92e5 100644 --- a/post-processor/amazon-import/post-processor.hcl2spec.go +++ b/post-processor/amazon-import/post-processor.hcl2spec.go @@ -30,6 +30,7 @@ type FlatConfig struct { SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` S3Bucket *string `mapstructure:"s3_bucket_name" cty:"s3_bucket_name" hcl:"s3_bucket_name"` S3Key *string `mapstructure:"s3_key_name" cty:"s3_key_name" hcl:"s3_key_name"` S3Encryption *string `mapstructure:"s3_encryption" cty:"s3_encryption" hcl:"s3_encryption"` @@ -79,6 +80,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())}, "s3_bucket_name": &hcldec.AttrSpec{Name: "s3_bucket_name", Type: cty.String, Required: false}, "s3_key_name": &hcldec.AttrSpec{Name: "s3_key_name", Type: cty.String, Required: false}, "s3_encryption": &hcldec.AttrSpec{Name: "s3_encryption", Type: cty.String, Required: false}, diff --git a/website/pages/docs/builders/amazon/chroot.mdx b/website/pages/docs/builders/amazon/chroot.mdx index 7062ee69d..0e93abd61 100644 --- a/website/pages/docs/builders/amazon/chroot.mdx +++ b/website/pages/docs/builders/amazon/chroot.mdx @@ -106,6 +106,12 @@ Block devices can be nested in the @include 'builder/amazon/common/AccessConfig-not-required.mdx' +### Polling Configuration + +@include 'builder/amazon/common/AWSPollingConfig.mdx' + +@include 'builder/amazon/common/AWSPollingConfig-not-required.mdx' + ## Basic Example Here is a basic example. It is completely valid except for the access keys: diff --git a/website/pages/docs/builders/amazon/ebs.mdx b/website/pages/docs/builders/amazon/ebs.mdx index 70c01e1f3..376068081 100644 --- a/website/pages/docs/builders/amazon/ebs.mdx +++ b/website/pages/docs/builders/amazon/ebs.mdx @@ -69,6 +69,12 @@ necessary for this build to succeed and can be found further down the page. @include 'builder/amazon/common/AccessConfig-not-required.mdx' +### Polling Configuration + +@include 'builder/amazon/common/AWSPollingConfig.mdx' + +@include 'builder/amazon/common/AWSPollingConfig-not-required.mdx' + ### Run Configuration #### Required: diff --git a/website/pages/docs/builders/amazon/ebssurrogate.mdx b/website/pages/docs/builders/amazon/ebssurrogate.mdx index 6e323a5ae..b6b7fe7d7 100644 --- a/website/pages/docs/builders/amazon/ebssurrogate.mdx +++ b/website/pages/docs/builders/amazon/ebssurrogate.mdx @@ -65,6 +65,12 @@ necessary for this build to succeed and can be found further down the page. @include 'builder/amazon/common/AccessConfig-not-required.mdx' +### Polling Configuration + +@include 'builder/amazon/common/AWSPollingConfig.mdx' + +@include 'builder/amazon/common/AWSPollingConfig-not-required.mdx' + ### Run Configuration #### Required: diff --git a/website/pages/docs/builders/amazon/ebsvolume.mdx b/website/pages/docs/builders/amazon/ebsvolume.mdx index a6d4d9b97..c7faadf14 100644 --- a/website/pages/docs/builders/amazon/ebsvolume.mdx +++ b/website/pages/docs/builders/amazon/ebsvolume.mdx @@ -57,6 +57,12 @@ necessary for this build to succeed and can be found further down the page. @include 'builder/amazon/common/AccessConfig-not-required.mdx' +### Polling Configuration + +@include 'builder/amazon/common/AWSPollingConfig.mdx' + +@include 'builder/amazon/common/AWSPollingConfig-not-required.mdx' + ### AMI Configuration #### Optional: diff --git a/website/pages/docs/builders/amazon/instance.mdx b/website/pages/docs/builders/amazon/instance.mdx index 805ff299a..76686ce95 100644 --- a/website/pages/docs/builders/amazon/instance.mdx +++ b/website/pages/docs/builders/amazon/instance.mdx @@ -85,6 +85,12 @@ necessary for this build to succeed and can be found further down the page. @include 'builder/amazon/common/AccessConfig-not-required.mdx' +### Polling Configuration + +@include 'builder/amazon/common/AWSPollingConfig.mdx' + +@include 'builder/amazon/common/AWSPollingConfig-not-required.mdx' + ### Run Configuration #### Required: diff --git a/website/pages/partials/builder/amazon/common/AWSPollingConfig-not-required.mdx b/website/pages/partials/builder/amazon/common/AWSPollingConfig-not-required.mdx new file mode 100644 index 000000000..1462788e2 --- /dev/null +++ b/website/pages/partials/builder/amazon/common/AWSPollingConfig-not-required.mdx @@ -0,0 +1,11 @@ + + +- `max_attempts` (int) - Specifies the maximum number of attempts the waiter will check for resource state. + This value can also be set via the AWS_MAX_ATTEMPTS. + If both option and environment variable are set, the max_attempts will be considered over the AWS_MAX_ATTEMPTS. + If none is set, defaults to AWS waiter default which is 40 max_attempts. + +- `delay_seconds` (int) - Specifies the delay in seconds between attempts to check the resource state. + This value can also be set via the AWS_POLL_DELAY_SECONDS. + If both option and environment variable are set, the delay_seconds will be considered over the AWS_POLL_DELAY_SECONDS. + If none is set, defaults to AWS waiter default which is 15 seconds. diff --git a/website/pages/partials/builder/amazon/common/AWSPollingConfig.mdx b/website/pages/partials/builder/amazon/common/AWSPollingConfig.mdx new file mode 100644 index 000000000..5fbab730c --- /dev/null +++ b/website/pages/partials/builder/amazon/common/AWSPollingConfig.mdx @@ -0,0 +1,21 @@ + + +Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching +volumes or importing image. +Usage example: + +In JSON: +```json +"aws_polling" : { + "delay_seconds": 30, + "max_attempts": 50 +} +``` + +In HCL2: +```hcl +aws_polling { + delay_seconds = 30 + max_attempts = 50 +} +``` diff --git a/website/pages/partials/builder/amazon/common/AccessConfig-not-required.mdx b/website/pages/partials/builder/amazon/common/AccessConfig-not-required.mdx index 3c0faabe7..faadd849b 100644 --- a/website/pages/partials/builder/amazon/common/AccessConfig-not-required.mdx +++ b/website/pages/partials/builder/amazon/common/AccessConfig-not-required.mdx @@ -81,3 +81,6 @@ ttl = "3600s" } ``` + +- `aws_polling` (\*AWSPollingConfig) - [Polling configuration](#polling-configuration) for the AWS waiter. Configures the waiter that checks + resource state. diff --git a/website/pages/partials/builder/amazon/common/StateChangeConf.mdx b/website/pages/partials/builder/amazon/common/StateChangeConf.mdx new file mode 100644 index 000000000..33237d2d4 --- /dev/null +++ b/website/pages/partials/builder/amazon/common/StateChangeConf.mdx @@ -0,0 +1,3 @@ + + +StateChangeConf is the configuration struct used for `WaitForState`.