diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index e0a7ff1..ea04312 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.go b/builder/tencentcloud/cvm/builder.go index d814e8f..0ac3a8c 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -101,6 +101,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) &stepPreValidate{ b.config.SkipCreateImage, }, + &stepCheckSourceImageFamily{ + b.config.SourceImageId, + b.config.SourceImageName, + }, &stepCheckSourceImage{ b.config.SourceImageId, }, diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index ba1cd00..1009e46 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/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 65ee5c3..4269ed0 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:"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. @@ -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_family.go b/builder/tencentcloud/cvm/step_check_source_image_family.go new file mode 100644 index 0000000..01fe46d --- /dev/null +++ b/builder/tencentcloud/cvm/step_check_source_image_family.go @@ -0,0 +1,55 @@ +// 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 +} + +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.SourceImageFamily + + 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") + } + + 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) + 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, 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), "") +} + +func (s *stepCheckSourceImageFamily) Cleanup(bag multistep.StateBag) {} diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx index 053a2b9..c98e844 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` diff --git a/go.sum b/go.sum index f9e0ff2..a321d87 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=