diff --git a/builder/openstack/builder.hcl2spec.go b/builder/openstack/builder.hcl2spec.go index cdc9e1b9..0de96ca3 100644 --- a/builder/openstack/builder.hcl2spec.go +++ b/builder/openstack/builder.hcl2spec.go @@ -129,6 +129,7 @@ type FlatConfig struct { VolumeAvailabilityZone *string `mapstructure:"volume_availability_zone" required:"false" cty:"volume_availability_zone" hcl:"volume_availability_zone"` OpenstackProvider *string `mapstructure:"openstack_provider" cty:"openstack_provider" hcl:"openstack_provider"` UseFloatingIp *bool `mapstructure:"use_floating_ip" required:"false" cty:"use_floating_ip" hcl:"use_floating_ip"` + ImageCreationWait *int `mapstructure:"image_creation_wait" required:"false" cty:"image_creation_wait" hcl:"image_creation_wait"` } // FlatMapstructure returns a new FlatConfig. @@ -261,6 +262,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "volume_availability_zone": &hcldec.AttrSpec{Name: "volume_availability_zone", Type: cty.String, Required: false}, "openstack_provider": &hcldec.AttrSpec{Name: "openstack_provider", Type: cty.String, Required: false}, "use_floating_ip": &hcldec.AttrSpec{Name: "use_floating_ip", Type: cty.Bool, Required: false}, + "image_creation_wait": &hcldec.AttrSpec{Name: "image_creation_wait", Type: cty.Number, Required: false}, } return s } diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index 90c41753..3f6092eb 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -177,6 +177,9 @@ type RunConfig struct { OpenstackProvider string `mapstructure:"openstack_provider"` // *Deprecated* use `floating_ip` or `floating_ip_pool` instead. UseFloatingIp bool `mapstructure:"use_floating_ip" required:"false"` + // Delay to apply once image has been marked as `active` to ensure Openstack has had time + // to finish creating the image. + ImageCreationWait int `mapstructure:"image_creation_wait" required:"false"` sourceImageOpts images.ListOpts } diff --git a/builder/openstack/step_create_image.go b/builder/openstack/step_create_image.go index e4af67ee..b9a938de 100644 --- a/builder/openstack/step_create_image.go +++ b/builder/openstack/step_create_image.go @@ -105,7 +105,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul // Wait for the image to become ready ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId)) - if err := WaitForImage(ctx, imageClient, imageId); err != nil { + if err := WaitForImage(ctx, imageClient, imageId, config.ImageCreationWait); err != nil { err := fmt.Errorf("Error waiting for image: %s", err) state.Put("error", err) ui.Error(err.Error()) @@ -120,7 +120,7 @@ func (s *stepCreateImage) Cleanup(multistep.StateBag) { } // WaitForImage waits for the given Image ID to become ready. -func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageId string) error { +func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageId string, imageCreationWait int) error { maxNumErrors := 10 numErrors := 0 @@ -146,6 +146,10 @@ func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageI } if image.Status == "active" { + if imageCreationWait > 0 { + log.Printf("Additional wait (%d seconds) after image status has changed to active...", imageCreationWait) + time.Sleep(time.Duration(imageCreationWait) * time.Second) + } return nil } diff --git a/docs-partials/builder/openstack/RunConfig-not-required.mdx b/docs-partials/builder/openstack/RunConfig-not-required.mdx index db7698fd..596ad272 100644 --- a/docs-partials/builder/openstack/RunConfig-not-required.mdx +++ b/docs-partials/builder/openstack/RunConfig-not-required.mdx @@ -96,4 +96,7 @@ - `use_floating_ip` (bool) - *Deprecated* use `floating_ip` or `floating_ip_pool` instead. +- `image_creation_wait` (int) - Delay to apply once image has been marked as `active` to ensure Openstack has had time + to finish creating the image. +