From 47905214951b94e98b5d757296e3001c9060f0cf Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Thu, 5 Dec 2024 09:21:16 +0200 Subject: [PATCH 1/4] Store the created image in state instead of locally in stepCreateImage --- builder/tencentcloud/cvm/step_create_image.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index b337fbeb..ae145545 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -12,7 +12,6 @@ import ( ) type stepCreateImage struct { - imageId string } func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -95,21 +94,18 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul return Halt(state, fmt.Errorf("No image return"), "Failed to crate image") } - s.imageId = *image.ImageId state.Put("image", image) - Message(state, s.imageId, "Image created") + Message(state, *image.ImageId, "Image created") tencentCloudImages := make(map[string]string) - tencentCloudImages[config.Region] = s.imageId + tencentCloudImages[config.Region] = *image.ImageId state.Put("tencentcloudimages", tencentCloudImages) return multistep.ActionContinue } func (s *stepCreateImage) Cleanup(state multistep.StateBag) { - if s.imageId == "" { - return - } + imageId := state.Get("image").(*cvm.Image).ImageId _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) @@ -123,12 +119,12 @@ func (s *stepCreateImage) Cleanup(state multistep.StateBag) { SayClean(state, "image") req := cvm.NewDeleteImagesRequest() - req.ImageIds = []*string{&s.imageId} + req.ImageIds = []*string{imageId} err := Retry(ctx, func(ctx context.Context) error { _, e := client.DeleteImages(req) return e }) if err != nil { - Error(state, err, fmt.Sprintf("Failed to delete image(%s), please delete it manually", s.imageId)) + Error(state, err, fmt.Sprintf("Failed to delete image(%s), please delete it manually", imageId)) } } From ef3c4d479e60b77efa969f90d8217738eb354445 Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Thu, 5 Dec 2024 09:23:45 +0200 Subject: [PATCH 2/4] Fix typo in DestinationRegions --- builder/tencentcloud/cvm/step_copy_image.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builder/tencentcloud/cvm/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go index bc481e87..0ac5946a 100644 --- a/builder/tencentcloud/cvm/step_copy_image.go +++ b/builder/tencentcloud/cvm/step_copy_image.go @@ -14,8 +14,8 @@ import ( ) type stepCopyImage struct { - DesinationRegions []string - SourceRegion string + DestinationRegions []string + SourceRegion string } func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -28,12 +28,12 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi imageId := state.Get("image").(*cvm.Image).ImageId - Say(state, strings.Join(s.DesinationRegions, ","), "Trying to copy image to") + Say(state, strings.Join(s.DestinationRegions, ","), "Trying to copy image to") req := cvm.NewSyncImagesRequest() req.ImageIds = []*string{imageId} - copyRegions := make([]*string, 0, len(s.DesinationRegions)) - for _, region := range s.DesinationRegions { + copyRegions := make([]*string, 0, len(s.DestinationRegions)) + for _, region := range s.DestinationRegions { if region != s.SourceRegion { copyRegions = append(copyRegions, common.StringPtr(region)) } From db1d7e387bf1fb7ab4ae332351bb64df42dd034f Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Thu, 5 Dec 2024 09:32:27 +0200 Subject: [PATCH 3/4] Add "skip_create_image" option Fixes #143 --- .web-docs/components/builder/cvm/README.md | 2 ++ builder/tencentcloud/cvm/builder.go | 8 +++++--- builder/tencentcloud/cvm/builder.hcl2spec.go | 2 ++ builder/tencentcloud/cvm/image_config.go | 2 ++ builder/tencentcloud/cvm/step_copy_image.go | 7 +++++-- builder/tencentcloud/cvm/step_create_image.go | 15 ++++++++++++++- builder/tencentcloud/cvm/step_share_image.go | 11 +++++++++-- .../cvm/TencentCloudImageConfig-not-required.mdx | 2 ++ 8 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 227bbb61..f849bde9 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -101,6 +101,8 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `skip_create_image` (bool) - Skip creating the image. Defaults to `false`. + diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index cda74a0c..4288d0d5 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -145,13 +145,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) // We need this step to detach keypair from instance, otherwise // it always fails to delete the key. &stepDetachTempKeyPair{}, - &stepCreateImage{}, + &stepCreateImage{ + b.config.SkipCreateImage, + }, &stepShareImage{ b.config.ImageShareAccounts, }, &stepCopyImage{ - DesinationRegions: b.config.ImageCopyRegions, - SourceRegion: b.config.Region, + DestinationRegions: b.config.ImageCopyRegions, + SourceRegion: b.config.Region, }, } diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 51eadf70..770c338b 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -36,6 +36,7 @@ type FlatConfig struct { ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions" hcl:"image_copy_regions"` ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false" cty:"image_share_accounts" hcl:"image_share_accounts"` ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"` + SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"` AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"` SourceImageId *string `mapstructure:"source_image_id" required:"false" cty:"source_image_id" hcl:"source_image_id"` SourceImageName *string `mapstructure:"source_image_name" required:"false" cty:"source_image_name" hcl:"source_image_name"` @@ -152,6 +153,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_copy_regions": &hcldec.AttrSpec{Name: "image_copy_regions", Type: cty.List(cty.String), Required: false}, "image_share_accounts": &hcldec.AttrSpec{Name: "image_share_accounts", Type: cty.List(cty.String), Required: false}, "image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.Map(cty.String), Required: false}, + "skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/image_config.go b/builder/tencentcloud/cvm/image_config.go index 036c30dd..9f589ca7 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -33,6 +33,8 @@ type TencentCloudImageConfig struct { // Key/value pair tags that will be applied to the resulting image. ImageTags map[string]string `mapstructure:"image_tags" required:"false"` skipValidation bool + // Skip creating the image. Defaults to `false`. + SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"` } func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/tencentcloud/cvm/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go index 0ac5946a..321dc1c5 100644 --- a/builder/tencentcloud/cvm/step_copy_image.go +++ b/builder/tencentcloud/cvm/step_copy_image.go @@ -19,14 +19,17 @@ type stepCopyImage struct { } func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - if len(s.DesinationRegions) == 0 || (len(s.DesinationRegions) == 1 && s.DesinationRegions[0] == s.SourceRegion) { + image, ok := state.GetOk("image") + + // Skip if we don't have an image to copy or no regions to copy to + if !ok || len(s.DestinationRegions) == 0 || (len(s.DestinationRegions) == 1 && s.DestinationRegions[0] == s.SourceRegion) { return multistep.ActionContinue } config := state.Get("config").(*Config) client := state.Get("cvm_client").(*cvm.Client) - imageId := state.Get("image").(*cvm.Image).ImageId + imageId := image.(*cvm.Image).ImageId Say(state, strings.Join(s.DestinationRegions, ","), "Trying to copy image to") diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index ae145545..dc9d7b26 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -12,6 +12,7 @@ import ( ) type stepCreateImage struct { + SkipCreateImage bool } func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -20,6 +21,12 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul config := state.Get("config").(*Config) instance := state.Get("instance").(*cvm.Instance) + // Optionally skip this step + if s.SkipCreateImage { + Say(state, "Skipping image creation step", "") + return multistep.ActionContinue + } + Say(state, config.ImageName, "Trying to create a new image") req := cvm.NewCreateImageRequest() @@ -105,7 +112,13 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul } func (s *stepCreateImage) Cleanup(state multistep.StateBag) { - imageId := state.Get("image").(*cvm.Image).ImageId + // Skip cleanup if we never created an image + image, ok := state.GetOk("image") + if !ok { + return + } + + imageId := image.(*cvm.Image).ImageId _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) diff --git a/builder/tencentcloud/cvm/step_share_image.go b/builder/tencentcloud/cvm/step_share_image.go index 821555bf..48819f83 100644 --- a/builder/tencentcloud/cvm/step_share_image.go +++ b/builder/tencentcloud/cvm/step_share_image.go @@ -18,7 +18,9 @@ type stepShareImage struct { } func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - if len(s.ShareAccounts) == 0 { + // Skip step if we don't have an image to share or no accounts to share to + _, ok := state.GetOk("image") + if !ok || len(s.ShareAccounts) == 0 { return multistep.ActionContinue } @@ -58,7 +60,12 @@ func (s *stepShareImage) Cleanup(state multistep.StateBag) { ctx := context.TODO() client := state.Get("cvm_client").(*cvm.Client) - imageId := state.Get("image").(*cvm.Image).ImageId + image, ok := state.GetOk("image") + if !ok { + return + } + + imageId := image.(*cvm.Image).ImageId SayClean(state, "image share") req := cvm.NewModifyImageSharePermissionRequest() diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index b1c3baf0..8a033055 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -15,4 +15,6 @@ - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `skip_create_image` (bool) - Skip creating the image. Defaults to `false`. + From f6bf78ec6bc5cb2922274b9ba570e22e63f992ae Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Thu, 5 Dec 2024 09:26:38 +0200 Subject: [PATCH 4/4] Skip superfluous image name validation when "skip_create_image" is true --- builder/tencentcloud/cvm/builder.go | 4 +++- builder/tencentcloud/cvm/step_pre_validate.go | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 4288d0d5..e7b0f5ce 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -90,7 +90,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) // Build the steps var steps []multistep.Step steps = []multistep.Step{ - &stepPreValidate{}, + &stepPreValidate{ + b.config.SkipCreateImage, + }, &stepCheckSourceImage{ b.config.SourceImageId, }, diff --git a/builder/tencentcloud/cvm/step_pre_validate.go b/builder/tencentcloud/cvm/step_pre_validate.go index 4f654e93..4365c4d2 100644 --- a/builder/tencentcloud/cvm/step_pre_validate.go +++ b/builder/tencentcloud/cvm/step_pre_validate.go @@ -12,9 +12,15 @@ import ( ) type stepPreValidate struct { + SkipCreateImage bool } func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + // No need to validate the image name if we're not creating an image + if s.SkipCreateImage { + return multistep.ActionContinue + } + config := state.Get("config").(*Config) client := state.Get("cvm_client").(*cvm.Client)