From 5332df30d408fb635e9737ee520f1a38214dea9e Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 26 Sep 2025 17:13:04 +0800 Subject: [PATCH 1/4] support source_image_family --- builder/tencentcloud/cvm/builder.go | 7 +++ builder/tencentcloud/cvm/run_config.go | 12 +++- .../cvm/step_check_source_image.go | 7 ++- .../cvm/step_check_source_image_family.go | 56 +++++++++++++++++++ go.sum | 4 ++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 builder/tencentcloud/cvm/step_check_source_image_family.go diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index d814e8fb..2e50860e 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -101,8 +101,15 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) &stepPreValidate{ b.config.SkipCreateImage, }, + &stepCheckSourceImageFamily{ + b.config.SourceImageId, + b.config.SourceImageName, + b.config.SourceImageFamily, + }, &stepCheckSourceImage{ b.config.SourceImageId, + b.config.SourceImageName, + b.config.SourceImageFamily, }, &stepConfigKeyPair{ Debug: b.config.PackerDebug, diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 65ee5c3a..5c364e2f 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -29,11 +29,17 @@ type TencentCloudRunConfig struct { // Default value is `false`. AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address" required:"false"` // The base image id of Image you want to create + // You can also specify `source_image_family`. If both `source_image` and `source_image_family` are specified, `source_image` takes precedence. // your customized image from. SourceImageId string `mapstructure:"source_image_id" required:"false"` // The base image name of Image you want to create your - // customized image from.Conflict with SourceImageId. + // customized image from.Conflict with SourceImageId and SourceImageName. SourceImageName string `mapstructure:"source_image_name" required:"false"` + // The source image family to use to create the new image from. + // The image family always returns its latest image that is not deprecated. + // Conflict with SourceImageId and SourceImageName. It takes effect when SourceImageId and SourceImageName are empty. + // Example value: business-daily-update. + SourceImageFamily string `mapstructure:"image_family" required:"false"` // Charge type of cvm, values can be `POSTPAID_BY_HOUR` (default) `SPOTPAID` InstanceChargeType string `mapstructure:"instance_charge_type" required:"false"` // The instance type your cvm will be launched by. @@ -133,8 +139,8 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { errs = append(errs, errors.New("zone must be specified")) } - if cf.SourceImageId == "" && cf.SourceImageName == "" { - errs = append(errs, errors.New("source_image_id or source_image_name must be specified")) + if cf.SourceImageId == "" && cf.SourceImageName == "" && cf.SourceImageFamily == "" { + errs = append(errs, errors.New("source_image_id or source_image_name or source_image_family must be specified")) } if cf.SourceImageId != "" && !CheckResourceIdFormat("img", cf.SourceImageId) { diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go index 616c4734..ce3d7762 100644 --- a/builder/tencentcloud/cvm/step_check_source_image.go +++ b/builder/tencentcloud/cvm/step_check_source_image.go @@ -13,10 +13,15 @@ import ( ) type stepCheckSourceImage struct { - sourceImageId string + sourceImageId string + sourceImageName string + sourceImageFamily string } func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + if s.sourceImageId == "" && s.sourceImageName == "" && s.sourceImageFamily != "" { + return multistep.ActionContinue + } var ( imageNameRegex *regexp.Regexp err error diff --git a/builder/tencentcloud/cvm/step_check_source_image_family.go b/builder/tencentcloud/cvm/step_check_source_image_family.go new file mode 100644 index 00000000..d4c743ec --- /dev/null +++ b/builder/tencentcloud/cvm/step_check_source_image_family.go @@ -0,0 +1,56 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cvm + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" +) + +type stepCheckSourceImageFamily struct { + sourceImageId string + sourceImageName string + sourceImageFamily string +} + +func (s *stepCheckSourceImageFamily) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + if s.sourceImageId != "" || s.sourceImageName != "" { + return multistep.ActionContinue + } + config := state.Get("config").(*Config) + client := state.Get("cvm_client").(*cvm.Client) + + Say(state, config.SourceImageFamily, "Try to check the source image and get the latest valid image of the image family") + + req := cvm.NewDescribeImageFromFamilyRequest() + req.ImageFamily = &config.InstanceType + + var resp *cvm.DescribeImageFromFamilyResponse + err := Retry(ctx, func(ctx context.Context) error { + var err error + resp, err = client.DescribeImageFromFamily(req) + return err + }) + if err != nil { + return Halt(state, err, "Failed to get source image info from the image family") + } + + image := resp.Response.Image + if image != nil { + if image.ImageId != nil && !*image.ImageDeprecated { + state.Put("source_image", image.ImageId) + Message(state, fmt.Sprintf("Get the latest image from the image family, id: %v", *image.ImageId), "Image found") + return multistep.ActionContinue + } + } else { + return Halt(state, err, "Failed to get source image info from the image family") + } + + return Halt(state, fmt.Errorf("No image found under current instance_type(%s) restriction", config.InstanceType), "") +} + +func (s *stepCheckSourceImageFamily) Cleanup(bag multistep.StateBag) {} diff --git a/go.sum b/go.sum index f9e0ff20..a321d879 100644 --- a/go.sum +++ b/go.sum @@ -314,8 +314,12 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1175 h1:w0z github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1175/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1200 h1:n6elge0PuoOtHt67BhlQka5Y7ChPbCtp23zYDFw56V0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1200/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.31 h1:PKa4c2BLYbW5LUOWGNXt20+rV9L8JnLqBXZjnOXsHKQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.31/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.1072 h1:BO6eEqw2CxeP73AnOfkq99mJAPccP+w3exmVuBDMvAc= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.1072/go.mod h1:Ot4h9BuqIbyOPotq8cIT3vCCMDdn6LXkc37jU2teHnQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.1.31 h1:M6v6WE88puzkxap8QgVSHM3u2Pe80E8uwps8U08FOOk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.1.31/go.mod h1:oMQNF1IsVtrOHdBONFRCWz0T5zqmxzM6JDBPl8ub6EM= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization v1.0.1200 h1:hYOvCfgPKpH2OS6+6ZOT+h21CfduIbGfz7RE+Ey1H14= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization v1.0.1200/go.mod h1:qYzdOsPWtOJ18uQ/4QupBTF98ariELEH4dx93GiBeuY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.797 h1:Z9rTZBoR4arEXA9gYLu8AQnMInG1scb+WnlIWczLH2A= From ff75b75d1b47a792458c5865c0050cf38df6e6df Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 26 Sep 2025 17:39:12 +0800 Subject: [PATCH 2/4] fix: modify source_image_family --- builder/tencentcloud/cvm/run_config.go | 2 +- builder/tencentcloud/cvm/step_check_source_image.go | 3 --- .../tencentcloud/cvm/step_check_source_image_family.go | 10 +++++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 5c364e2f..4269ed0d 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -39,7 +39,7 @@ type TencentCloudRunConfig struct { // The image family always returns its latest image that is not deprecated. // Conflict with SourceImageId and SourceImageName. It takes effect when SourceImageId and SourceImageName are empty. // Example value: business-daily-update. - SourceImageFamily string `mapstructure:"image_family" required:"false"` + SourceImageFamily string `mapstructure:"source_image_family" required:"false"` // Charge type of cvm, values can be `POSTPAID_BY_HOUR` (default) `SPOTPAID` InstanceChargeType string `mapstructure:"instance_charge_type" required:"false"` // The instance type your cvm will be launched by. diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go index ce3d7762..2cc768fd 100644 --- a/builder/tencentcloud/cvm/step_check_source_image.go +++ b/builder/tencentcloud/cvm/step_check_source_image.go @@ -19,9 +19,6 @@ type stepCheckSourceImage struct { } func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - if s.sourceImageId == "" && s.sourceImageName == "" && s.sourceImageFamily != "" { - return multistep.ActionContinue - } var ( imageNameRegex *regexp.Regexp err error diff --git a/builder/tencentcloud/cvm/step_check_source_image_family.go b/builder/tencentcloud/cvm/step_check_source_image_family.go index d4c743ec..f0a5d198 100644 --- a/builder/tencentcloud/cvm/step_check_source_image_family.go +++ b/builder/tencentcloud/cvm/step_check_source_image_family.go @@ -27,7 +27,7 @@ func (s *stepCheckSourceImageFamily) Run(ctx context.Context, state multistep.St Say(state, config.SourceImageFamily, "Try to check the source image and get the latest valid image of the image family") req := cvm.NewDescribeImageFromFamilyRequest() - req.ImageFamily = &config.InstanceType + req.ImageFamily = &config.SourceImageFamily var resp *cvm.DescribeImageFromFamilyResponse err := Retry(ctx, func(ctx context.Context) error { @@ -39,15 +39,15 @@ func (s *stepCheckSourceImageFamily) Run(ctx context.Context, state multistep.St return Halt(state, err, "Failed to get source image info from the image family") } - image := resp.Response.Image - if image != nil { + if resp != nil && resp.Response != nil && resp.Response.Image != nil { + image := resp.Response.Image if image.ImageId != nil && !*image.ImageDeprecated { - state.Put("source_image", image.ImageId) + state.Put("source_image", image) Message(state, fmt.Sprintf("Get the latest image from the image family, id: %v", *image.ImageId), "Image found") return multistep.ActionContinue } } else { - return Halt(state, err, "Failed to get source image info from the image family") + return Halt(state, fmt.Errorf("failed to get source image: %v", resp.ToJsonString()), "No image family found") } return Halt(state, fmt.Errorf("No image found under current instance_type(%s) restriction", config.InstanceType), "") From 412ca35323abbe45461123f8cf5d351e26e04ae5 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 26 Sep 2025 17:42:15 +0800 Subject: [PATCH 3/4] fix: modify source_image_family --- builder/tencentcloud/cvm/builder.go | 3 --- builder/tencentcloud/cvm/step_check_source_image.go | 4 +--- builder/tencentcloud/cvm/step_check_source_image_family.go | 5 ++--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 2e50860e..0ac3a8c0 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -104,12 +104,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) &stepCheckSourceImageFamily{ b.config.SourceImageId, b.config.SourceImageName, - b.config.SourceImageFamily, }, &stepCheckSourceImage{ b.config.SourceImageId, - b.config.SourceImageName, - b.config.SourceImageFamily, }, &stepConfigKeyPair{ Debug: b.config.PackerDebug, diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go index 2cc768fd..616c4734 100644 --- a/builder/tencentcloud/cvm/step_check_source_image.go +++ b/builder/tencentcloud/cvm/step_check_source_image.go @@ -13,9 +13,7 @@ import ( ) type stepCheckSourceImage struct { - sourceImageId string - sourceImageName string - sourceImageFamily string + sourceImageId string } func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { diff --git a/builder/tencentcloud/cvm/step_check_source_image_family.go b/builder/tencentcloud/cvm/step_check_source_image_family.go index f0a5d198..01fe46d9 100644 --- a/builder/tencentcloud/cvm/step_check_source_image_family.go +++ b/builder/tencentcloud/cvm/step_check_source_image_family.go @@ -12,9 +12,8 @@ import ( ) type stepCheckSourceImageFamily struct { - sourceImageId string - sourceImageName string - sourceImageFamily string + sourceImageId string + sourceImageName string } func (s *stepCheckSourceImageFamily) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { From 2c63ddc006edaca2211a2aabf69219491a9c25b2 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 26 Sep 2025 17:43:02 +0800 Subject: [PATCH 4/4] update doc --- .web-docs/components/builder/cvm/README.md | 8 +++++++- builder/tencentcloud/cvm/builder.hcl2spec.go | 2 ++ .../cvm/TencentCloudRunConfig-not-required.mdx | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index e0a7ff14..ea04312c 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -128,10 +128,16 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can Default value is `false`. - `source_image_id` (string) - The base image id of Image you want to create + You can also specify `source_image_family`. If both `source_image` and `source_image_family` are specified, `source_image` takes precedence. your customized image from. - `source_image_name` (string) - The base image name of Image you want to create your - customized image from.Conflict with SourceImageId. + customized image from.Conflict with SourceImageId and SourceImageName. + +- `source_image_family` (string) - The source image family to use to create the new image from. + The image family always returns its latest image that is not deprecated. + Conflict with SourceImageId and SourceImageName. It takes effect when SourceImageId and SourceImageName are empty. + Example value: business-daily-update. - `instance_charge_type` (string) - Charge type of cvm, values can be `POSTPAID_BY_HOUR` (default) `SPOTPAID` diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index ba1cd00c..1009e462 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -44,6 +44,7 @@ type FlatConfig struct { 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"` + SourceImageFamily *string `mapstructure:"source_image_family" required:"false" cty:"source_image_family" hcl:"source_image_family"` InstanceChargeType *string `mapstructure:"instance_charge_type" required:"false" cty:"instance_charge_type" hcl:"instance_charge_type"` InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type" hcl:"instance_type"` InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"` @@ -167,6 +168,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false}, "instance_charge_type": &hcldec.AttrSpec{Name: "instance_charge_type", Type: cty.String, Required: false}, "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx index 053a2b94..c98e844a 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx @@ -4,10 +4,16 @@ Default value is `false`. - `source_image_id` (string) - The base image id of Image you want to create + You can also specify `source_image_family`. If both `source_image` and `source_image_family` are specified, `source_image` takes precedence. your customized image from. - `source_image_name` (string) - The base image name of Image you want to create your - customized image from.Conflict with SourceImageId. + customized image from.Conflict with SourceImageId and SourceImageName. + +- `source_image_family` (string) - The source image family to use to create the new image from. + The image family always returns its latest image that is not deprecated. + Conflict with SourceImageId and SourceImageName. It takes effect when SourceImageId and SourceImageName are empty. + Example value: business-daily-update. - `instance_charge_type` (string) - Charge type of cvm, values can be `POSTPAID_BY_HOUR` (default) `SPOTPAID`