diff --git a/.github/workflows/sync-code.yaml b/.github/workflows/sync-code.yaml deleted file mode 100644 index 63cbd4e..0000000 --- a/.github/workflows/sync-code.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: sync-code - -on: - push: - branches: - - main - - 'coding-test/**' - - 'pipeline/**' - tags: v* - -jobs: - sync: - runs-on: ubuntu-latest - - steps: - - name: clone - run: | - if [ -d packer-plugin-tencentcloud ]; then - rm -rf packer-plugin-tencentcloud - fi - git clone https://github.com/tencentcloudstack/packer-plugin-tencentcloud.git - - name: sync - run: | - cd packer-plugin-tencentcloud - git fetch --all - git branch -r --list "origin/coding-test/*" | grep -v HEAD | grep -v master | xargs -I {} git checkout -t {} - git branch -r --list "origin/pipeline/*" | grep -v HEAD | grep -v master | xargs -I {} git checkout -t {} - git remote add tgit_origin https://${{ secrets.USERNAME }}:${{ secrets.PASSWORD }}@git.code.tencent.com/tencentcloudstack/packer-plugin-tencentcloud.git - git push -u tgit_origin -f --all - git push -u tgit_origin main:main - git push -u tgit_origin --tags diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index c35b143..c7f60fd 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -24,10 +24,6 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) for parameter taking. -- `zone` (string) - The zone where your cvm will be launch. You should - reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) - for parameter taking. - @@ -37,6 +33,10 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can You should reference [Instance Type](https://intl.cloud.tencent.com/document/product/213/11518) for parameter taking. +- `zone` (string) - The zone where your cvm will be launch. You should + reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) + for parameter taking. + @@ -59,6 +59,9 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `vpc_endpoint` (string) - The endpoint you want to reach the cloud endpoint, if tce cloud you should set a tce vpc endpoint. +- `tag_endpoint` (string) - The endpoint you want to reach the cloud endpoint, + if tce cloud you should set a tce tag endpoint. + - `security_token` (string) - STS access token, can be set through template or by exporting as environment variable such as `export TENCENTCLOUD_SECURITY_TOKEN=value`. @@ -101,6 +104,10 @@ 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. +- `snapshot_tags` (map[string]string) - Key/value pair tags that will be applied to snapshot. + +- `skip_create_image` (bool) - Skip creating an image. When set to true, you don't need to enter target image information, share, copy, etc. The default value is false. + diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go index 8954539..cecf859 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -7,13 +7,13 @@ package cvm import ( - "context" "fmt" "os" "strconv" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" ) @@ -76,16 +76,15 @@ type TencentCloudAccessConfig struct { // reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) // for parameter taking. Region string `mapstructure:"region" required:"true"` - // The zone where your cvm will be launch. You should - // reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) - // for parameter taking. - Zone string `mapstructure:"zone" required:"true"` // The endpoint you want to reach the cloud endpoint, // if tce cloud you should set a tce cvm endpoint. CvmEndpoint string `mapstructure:"cvm_endpoint" required:"false"` // The endpoint you want to reach the cloud endpoint, // if tce cloud you should set a tce vpc endpoint. VpcEndpoint string `mapstructure:"vpc_endpoint" required:"false"` + // The endpoint you want to reach the cloud endpoint, + // if tce cloud you should set a tce tag endpoint. + TagEndpoint string `mapstructure:"tag_endpoint" required:"false"` // The region validation can be skipped if this value is true, the default // value is false. skipValidation bool @@ -126,47 +125,31 @@ type TencentCloudAccessRole struct { SessionDuration int `mapstructure:"session_duration" required:"false"` } -func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) { +func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, *tag.Client, error) { var ( err error cvm_client *cvm.Client vpc_client *vpc.Client - resp *cvm.DescribeZonesResponse + tag_client *tag.Client ) if err = cf.validateRegion(); err != nil { - return nil, nil, err - } - - if cf.Zone == "" { - return nil, nil, fmt.Errorf("parameter zone must be set") + return nil, nil, nil, err } if cvm_client, err = NewCvmClient(cf); err != nil { - return nil, nil, err + return nil, nil, nil, err } if vpc_client, err = NewVpcClient(cf); err != nil { - return nil, nil, err + return nil, nil, nil, err } - ctx := context.TODO() - err = Retry(ctx, func(ctx context.Context) error { - var e error - resp, e = cvm_client.DescribeZones(nil) - return e - }) - if err != nil { - return nil, nil, err - } - - for _, zone := range resp.Response.ZoneSet { - if cf.Zone == *zone.Zone { - return cvm_client, vpc_client, nil - } + if tag_client, err = NewTagClient(cf); err != nil { + return nil, nil, nil, err } - return nil, nil, fmt.Errorf("unknown zone: %s", cf.Zone) + return cvm_client, vpc_client, tag_client, nil } func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { @@ -176,9 +159,9 @@ func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { errs = append(errs, err) } - if (cf.CvmEndpoint != "" && cf.VpcEndpoint == "") || - (cf.CvmEndpoint == "" && cf.VpcEndpoint != "") { - errs = append(errs, fmt.Errorf("parameter cvm_endpoint and vpc_endpoint must be set simultaneously")) + if !((cf.CvmEndpoint == "" && cf.VpcEndpoint == "" && cf.TagEndpoint == "") || + (cf.CvmEndpoint != "" && cf.VpcEndpoint != "" && cf.TagEndpoint != "")) { + errs = append(errs, fmt.Errorf("parameter cvm_endpoint, vpc_endpoint and tag_endpoint must be set simultaneously")) } if cf.Region == "" { diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 55f4483..6718d3e 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -75,7 +75,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { } func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { - cvmClient, vpcClient, err := b.config.Client() + cvmClient, vpcClient, tagClient, err := b.config.Client() if err != nil { return nil, err } @@ -84,13 +84,16 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) state.Put("config", &b.config) state.Put("cvm_client", cvmClient) state.Put("vpc_client", vpcClient) + state.Put("tag_client", tagClient) state.Put("hook", hook) state.Put("ui", ui) // Build the steps var steps []multistep.Step steps = []multistep.Step{ - &stepPreValidate{}, + &stepPreValidate{ + b.config.SkipCreateImage, + }, &stepCheckSourceImage{ b.config.SourceImageId, }, @@ -147,13 +150,16 @@ 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{ + SkipCreateImage: b.config.SkipCreateImage, + }, &stepShareImage{ b.config.ImageShareAccounts, }, &stepCopyImage{ - DesinationRegions: b.config.ImageCopyRegions, - SourceRegion: b.config.Region, + DestinationRegions: b.config.ImageCopyRegions, + SourceRegion: b.config.Region, + SkipCreateImage: b.config.SkipCreateImage, }, } diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 13988ba..976a1d6 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -22,9 +22,9 @@ type FlatConfig struct { SecretId *string `mapstructure:"secret_id" required:"true" cty:"secret_id" hcl:"secret_id"` SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` - Zone *string `mapstructure:"zone" required:"true" cty:"zone" hcl:"zone"` CvmEndpoint *string `mapstructure:"cvm_endpoint" required:"false" cty:"cvm_endpoint" hcl:"cvm_endpoint"` VpcEndpoint *string `mapstructure:"vpc_endpoint" required:"false" cty:"vpc_endpoint" hcl:"vpc_endpoint"` + TagEndpoint *string `mapstructure:"tag_endpoint" required:"false" cty:"tag_endpoint" hcl:"tag_endpoint"` SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token" hcl:"security_token"` AssumeRole *FlatTencentCloudAccessRole `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"` Profile *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"` @@ -36,6 +36,8 @@ 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"` + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags" hcl:"snapshot_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"` @@ -113,6 +115,7 @@ type FlatConfig struct { WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` SSHPrivateIp *bool `mapstructure:"ssh_private_ip" cty:"ssh_private_ip" hcl:"ssh_private_ip"` + Zone *string `mapstructure:"zone" required:"true" cty:"zone" hcl:"zone"` SkipRegionValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"` } @@ -139,9 +142,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "secret_id": &hcldec.AttrSpec{Name: "secret_id", Type: cty.String, Required: false}, "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, - "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, "cvm_endpoint": &hcldec.AttrSpec{Name: "cvm_endpoint", Type: cty.String, Required: false}, "vpc_endpoint": &hcldec.AttrSpec{Name: "vpc_endpoint", Type: cty.String, Required: false}, + "tag_endpoint": &hcldec.AttrSpec{Name: "tag_endpoint", Type: cty.String, Required: false}, "security_token": &hcldec.AttrSpec{Name: "security_token", Type: cty.String, Required: false}, "assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*FlatTencentCloudAccessRole)(nil).HCL2Spec())}, "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, @@ -153,6 +156,8 @@ 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}, + "snapshot_tags": &hcldec.AttrSpec{Name: "snapshot_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}, @@ -230,6 +235,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_private_ip": &hcldec.AttrSpec{Name: "ssh_private_ip", Type: cty.Bool, Required: false}, + "zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false}, "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, } return s diff --git a/builder/tencentcloud/cvm/client.go b/builder/tencentcloud/cvm/client.go index 7f6bef2..1d83edc 100644 --- a/builder/tencentcloud/cvm/client.go +++ b/builder/tencentcloud/cvm/client.go @@ -8,6 +8,7 @@ import ( "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" sts "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts/v20180813" + tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" ) @@ -20,6 +21,7 @@ type TencentCloudClient struct { vpcConn *vpc.Client cvmConn *cvm.Client stsConn *sts.Client + tagConn *tag.Client } func (me *TencentCloudClient) UseVpcClient(cpf *profile.ClientProfile) *vpc.Client { @@ -53,3 +55,13 @@ func (me *TencentCloudClient) UseStsClient() *sts.Client { return me.stsConn } + +func (me *TencentCloudClient) UseTagClient(cpf *profile.ClientProfile) *tag.Client { + if me.tagConn != nil { + return me.tagConn + } + + me.tagConn, _ = tag.NewClient(me.Credential, me.Region, cpf) + + return me.tagConn +} diff --git a/builder/tencentcloud/cvm/common.go b/builder/tencentcloud/cvm/common.go index 13ae9cb..ff514ab 100644 --- a/builder/tencentcloud/cvm/common.go +++ b/builder/tencentcloud/cvm/common.go @@ -19,6 +19,7 @@ import ( "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" sts "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts/v20180813" + tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" ) @@ -78,6 +79,27 @@ func WaitForImageReady(ctx context.Context, client *cvm.Client, imageName string } } +func AddResourceTag(ctx context.Context, client *tag.Client, resourceName string, tags map[string]string) error { + request := tag.NewModifyResourceTagsRequest() + request.Resource = &resourceName + request.ReplaceTags = make([]*tag.Tag, 0, len(tags)) + for k, v := range tags { + key := k + value := v + replaceTag := &tag.Tag{ + TagKey: &key, + TagValue: &value, + } + request.ReplaceTags = append(request.ReplaceTags, replaceTag) + } + + err := Retry(ctx, func(ctx context.Context) error { + _, e := client.ModifyResourceTags(request) + return e + }) + return err +} + // GetImageByName get image by image name func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) (*cvm.Image, error) { req := cvm.NewDescribeImagesRequest() @@ -143,6 +165,22 @@ func NewVpcClient(cf *TencentCloudAccessConfig) (client *vpc.Client, err error) return } +// UseTagClient returns a new tag client +func NewTagClient(cf *TencentCloudAccessConfig) (client *tag.Client, err error) { + apiV3Conn, err := packerConfigClient(cf) + if err != nil { + return nil, err + } + + tagClientProfile, err := newClientProfile(cf.TagEndpoint) + if err != nil { + return nil, err + } + client = apiV3Conn.UseTagClient(tagClientProfile) + + return +} + // CheckResourceIdFormat check resource id format func CheckResourceIdFormat(resource string, id string) bool { regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource)) @@ -322,3 +360,10 @@ func IntUint64(i int) *uint64 { u := uint64(i) return &u } + +// BuildTagResourceName builds the Tencent Cloud specific name of a resource description. +// The format is `qcs:project_id:service_type:region:account:resource`. +// For more information, go to https://cloud.tencent.com/document/product/598/10606. +func BuildTagResourceName(serviceType, resourceType, region, id string) string { + return fmt.Sprintf("qcs::%s:%s:uin/:%s/%s", serviceType, region, resourceType, id) +} diff --git a/builder/tencentcloud/cvm/image_config.go b/builder/tencentcloud/cvm/image_config.go index 036c30d..75a948d 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -31,13 +31,21 @@ type TencentCloudImageConfig struct { // after your image created. ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false"` // Key/value pair tags that will be applied to the resulting image. - ImageTags map[string]string `mapstructure:"image_tags" required:"false"` + ImageTags map[string]string `mapstructure:"image_tags" required:"false"` + // Key/value pair tags that will be applied to snapshot. + SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"` skipValidation bool + // Skip creating an image. When set to true, you don't need to enter target image information, share, copy, etc. The default value is false. + SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"` } func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error { var errs []error + if cf.SkipCreateImage { + return nil + } + if cf.ImageName == "" { errs = append(errs, fmt.Errorf("image_name must be specified")) } else if utf8.RuneCountInString(cf.ImageName) > 60 { @@ -73,6 +81,9 @@ func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error { if cf.ImageTags == nil { cf.ImageTags = make(map[string]string) } + if cf.SnapshotTags == nil { + cf.SnapshotTags = make(map[string]string) + } if len(errs) > 0 { return errs diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index d7cc171..65ee5c3 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -109,6 +109,10 @@ type TencentCloudRunConfig struct { // Communicator settings Comm communicator.Config `mapstructure:",squash"` SSHPrivateIp bool `mapstructure:"ssh_private_ip"` + // The zone where your cvm will be launch. You should + // reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) + // for parameter taking. + Zone string `mapstructure:"zone" required:"true"` } var ValidCBSType = []string{ @@ -116,7 +120,8 @@ var ValidCBSType = []string{ } func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { - packerId := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()[:8]) + timeOrderedUUID := uuid.TimeOrderedUUID() + packerId := fmt.Sprintf("packer_%s_%s", timeOrderedUUID[:8], timeOrderedUUID[9:13]) if cf.Comm.SSHKeyPairName == "" && cf.Comm.SSHTemporaryKeyPairName == "" && cf.Comm.SSHPrivateKeyFile == "" && cf.Comm.SSHPassword == "" && cf.Comm.WinRMPassword == "" { //tencentcloud support key pair name length max to 25 @@ -124,6 +129,10 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { } errs := cf.Comm.Prepare(ctx) + if cf.Zone == "" { + 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")) } diff --git a/builder/tencentcloud/cvm/run_config_test.go b/builder/tencentcloud/cvm/run_config_test.go index 97536ea..711f974 100644 --- a/builder/tencentcloud/cvm/run_config_test.go +++ b/builder/tencentcloud/cvm/run_config_test.go @@ -13,6 +13,7 @@ import ( func testConfig() *TencentCloudRunConfig { return &TencentCloudRunConfig{ + Zone: "ap-guangzhou", SourceImageId: "img-qwer1234", InstanceType: "S3.SMALL2", Comm: communicator.Config{ diff --git a/builder/tencentcloud/cvm/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go index bc481e8..6e8f6b5 100644 --- a/builder/tencentcloud/cvm/step_copy_image.go +++ b/builder/tencentcloud/cvm/step_copy_image.go @@ -14,12 +14,16 @@ import ( ) type stepCopyImage struct { - DesinationRegions []string - SourceRegion string + DestinationRegions []string + SourceRegion string + SkipCreateImage bool } 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) { + if s.SkipCreateImage { + return multistep.ActionContinue + } + if len(s.DestinationRegions) == 0 || (len(s.DestinationRegions) == 1 && s.DestinationRegions[0] == s.SourceRegion) { return multistep.ActionContinue } @@ -28,12 +32,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)) } @@ -54,7 +58,6 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi cf := &TencentCloudAccessConfig{ SecretId: config.SecretId, SecretKey: config.SecretKey, - Zone: config.Zone, CvmEndpoint: config.CvmEndpoint, SecurityToken: config.SecurityToken, AssumeRole: TencentCloudAccessRole{ diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index b337fbe..3fd164a 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -9,18 +9,27 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" ) type stepCreateImage struct { - imageId string + imageId string + SkipCreateImage bool } func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { client := state.Get("cvm_client").(*cvm.Client) + tagClient := state.Get("tag_client").(*tag.Client) 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() @@ -99,6 +108,20 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul state.Put("image", image) Message(state, s.imageId, "Image created") + snapshotTags := config.SnapshotTags + if len(snapshotTags) > 0 { + for _, snapshot := range image.SnapshotSet { + if snapshot == nil || snapshot.SnapshotId == nil { + return Halt(state, err, "snapshot or snapshotId is nil") + } + resourceName := BuildTagResourceName("cvm", "snapshot", config.Region, *snapshot.SnapshotId) + err := AddResourceTag(ctx, tagClient, resourceName, snapshotTags) + if err != nil { + return Halt(state, err, fmt.Sprintf("Failed to set tag for snapshot(%s)", *snapshot.SnapshotId)) + } + } + } + tencentCloudImages := make(map[string]string) tencentCloudImages[config.Region] = s.imageId state.Put("tencentcloudimages", tencentCloudImages) diff --git a/builder/tencentcloud/cvm/step_pre_validate.go b/builder/tencentcloud/cvm/step_pre_validate.go index 4f654e9..4365c4d 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) diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index df2afd0..8220640 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -11,6 +11,7 @@ import ( "log" "github.com/hashicorp/packer-plugin-sdk/multistep" + "github.com/hashicorp/packer-plugin-sdk/uuid" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" ) @@ -130,7 +131,11 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul req.InternetAccessible.BandwidthPackageId = &s.BandwidthPackageId } } - req.InstanceName = &s.InstanceName + + // Generate a unique ClientToken for each RunInstances request + clientToken := uuid.TimeOrderedUUID() + req.ClientToken = &clientToken + loginSettings := cvm.LoginSettings{} if password != "" { loginSettings.Password = &password @@ -140,7 +145,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul } req.LoginSettings = &loginSettings req.SecurityGroupIds = []*string{&security_group_id} - req.ClientToken = &s.InstanceName + req.InstanceName = &s.InstanceName req.HostName = &s.HostName req.UserData = &userData req.CamRoleName = &s.CamRoleName @@ -178,7 +183,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul } s.instanceId = *resp.Response.InstanceIdSet[0] - Message(state, "Waiting for instance ready", "") + Message(state, fmt.Sprintf("Instance %s created, waiting for instance ready", s.instanceId), "") err = WaitForInstance(ctx, client, s.instanceId, "RUNNING", 1800) if err != nil { diff --git a/datasource/tencentcloud/examples/image.pkr.hcl b/datasource/tencentcloud/examples/image.pkr.hcl new file mode 100644 index 0000000..2ff5348 --- /dev/null +++ b/datasource/tencentcloud/examples/image.pkr.hcl @@ -0,0 +1,29 @@ +data "tencentcloud-image" "test-image" { + filters = { + image-type = "PRIVATE_IMAGE" + } + most_recent = true + region = "ap-guangzhou" +} + +locals { + id = data.tencentcloud-image.test-image.id + name = data.tencentcloud-image.test-image.name +} + +source "null" "basic-example" { + communicator = "none" +} + +build { + sources = [ + "source.null.basic-example" + ] + + provisioner "shell-local" { + inline = [ + "echo id: ${local.id}", + "echo name: ${local.name}", + ] + } +} \ No newline at end of file diff --git a/datasource/tencentcloud/image/data.go b/datasource/tencentcloud/image/data.go new file mode 100644 index 0000000..d096cc2 --- /dev/null +++ b/datasource/tencentcloud/image/data.go @@ -0,0 +1,178 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:generate packer-sdc mapstructure-to-hcl2 -type DatasourceOutput,Config,Image + +package image + +import ( + "context" + "fmt" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer-plugin-sdk/common" + "github.com/hashicorp/packer-plugin-sdk/hcl2helper" + packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/hashicorp/packer-plugin-sdk/template/config" + "github.com/hashicorp/packer-plugin-sdk/template/interpolate" + buildCvm "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm" + cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + "github.com/zclconf/go-cty/cty" +) + +type ImageFilterOptions struct { + // Filters used to select an image. Any filter described in the documentation for + // [DescribeImages](https://www.tencentcloud.com/document/product/213/33272) can be used. + Filters map[string]string `mapstructure:"filters"` + // Image family used to select an image. Uses the + // [DescribeImageFromFamily](https://www.tencentcloud.com/document/product/213/64971) API. + // Mutually exclusive with `filters`, and `most_recent` will have no effect. + ImageFamily string `mapstructure:"image_family"` + // Selects the most recently created image when multiple results are returned. Note that + // public images don't have a creation date, so this flag is only really useful for private + // images. + MostRecent bool `mapstructure:"most_recent"` +} + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + buildCvm.TencentCloudAccessConfig `mapstructure:",squash"` + ImageFilterOptions `mapstructure:",squash"` + ctx interpolate.Context +} + +type Datasource struct { + config Config +} + +func (d *Datasource) ConfigSpec() hcldec.ObjectSpec { + return d.config.FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Configure(raws ...interface{}) error { + err := config.Decode(&d.config, nil, raws...) + if err != nil { + return err + } + + var errs *packersdk.MultiError + errs = packersdk.MultiErrorAppend(errs, d.config.TencentCloudAccessConfig.Prepare(&d.config.ctx)...) + + if len(d.config.Filters) == 0 && d.config.ImageFamily == "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`filters` or `image_family` must be specified")) + } + + if len(d.config.Filters) > 0 && d.config.ImageFamily != "" { + errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("`filters` and `image_family` are mutually exclusive")) + } + + if errs != nil && len(errs.Errors) > 0 { + return errs + } + return nil +} + +type DatasourceOutput struct { + // The image ID + ID string `mapstructure:"id"` + // The image name + Name string `mapstructure:"name"` +} + +func (d *Datasource) OutputSpec() hcldec.ObjectSpec { + return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() +} + +func (d *Datasource) Execute() (cty.Value, error) { + var image *cvm.Image + var err error + + if len(d.config.Filters) > 0 { + image, err = d.ResolveImageByFilters() + } else { + image, err = d.ResolveImageByImageFamily() + } + + if err != nil { + return cty.NullVal(cty.EmptyObject), err + } + + output := DatasourceOutput{ + ID: *image.ImageId, + Name: *image.ImageName, + } + return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil +} + +func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { + client, _, _, err := d.config.Client() + if err != nil { + return nil, err + } + + req := cvm.NewDescribeImagesRequest() + + var filters []*cvm.Filter + for k, v := range d.config.Filters { + k := k + v := v + filters = append(filters, &cvm.Filter{ + Name: &k, + Values: []*string{&v}, + }) + } + req.Filters = filters + + ctx := context.TODO() + var resp *cvm.DescribeImagesResponse + err = buildCvm.Retry(ctx, func(ctx context.Context) error { + var e error + resp, e = client.DescribeImages(req) + return e + }) + if err != nil { + return nil, err + } + + if *resp.Response.TotalCount == 0 { + return nil, fmt.Errorf("No image found using the specified filters") + } + + if *resp.Response.TotalCount > 1 && !d.config.MostRecent { + return nil, fmt.Errorf("Your image query returned more than result. Please try a more specific search, or set `most_recent` to `true`.") + } + + if d.config.MostRecent { + return mostRecentImage(resp.Response.ImageSet), nil + } else { + return resp.Response.ImageSet[0], nil + } +} + +func (d *Datasource) ResolveImageByImageFamily() (*cvm.Image, error) { + client, _, _, err := d.config.Client() + if err != nil { + return nil, err + } + + var resp *cvm.DescribeImageFromFamilyResponse + req := cvm.NewDescribeImageFromFamilyRequest() + req.ImageFamily = &d.config.ImageFamily + + ctx := context.TODO() + err = buildCvm.Retry(ctx, func(ctx context.Context) error { + var e error + resp, e = client.DescribeImageFromFamily(req) + return e + }) + + if err != nil { + return nil, err + } + + if resp.Response.Image == nil { + return nil, fmt.Errorf("No image found using the specified image family") + } + + return resp.Response.Image, nil +} diff --git a/datasource/tencentcloud/image/data.hcl2spec.go b/datasource/tencentcloud/image/data.hcl2spec.go new file mode 100644 index 0000000..7327099 --- /dev/null +++ b/datasource/tencentcloud/image/data.hcl2spec.go @@ -0,0 +1,97 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package image + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + SecretId *string `mapstructure:"secret_id" required:"true" cty:"secret_id" hcl:"secret_id"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` + Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` + CvmEndpoint *string `mapstructure:"cvm_endpoint" required:"false" cty:"cvm_endpoint" hcl:"cvm_endpoint"` + VpcEndpoint *string `mapstructure:"vpc_endpoint" required:"false" cty:"vpc_endpoint" hcl:"vpc_endpoint"` + TagEndpoint *string `mapstructure:"tag_endpoint" required:"false" cty:"tag_endpoint" hcl:"tag_endpoint"` + SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token" hcl:"security_token"` + AssumeRole *cvm.FlatTencentCloudAccessRole `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"` + Profile *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"` + SharedCredentialsDir *string `mapstructure:"shared_credentials_dir" required:"false" cty:"shared_credentials_dir" hcl:"shared_credentials_dir"` + Filters map[string]string `mapstructure:"filters" cty:"filters" hcl:"filters"` + ImageFamily *string `mapstructure:"image_family" cty:"image_family" hcl:"image_family"` + MostRecent *bool `mapstructure:"most_recent" cty:"most_recent" hcl:"most_recent"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "secret_id": &hcldec.AttrSpec{Name: "secret_id", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "cvm_endpoint": &hcldec.AttrSpec{Name: "cvm_endpoint", Type: cty.String, Required: false}, + "vpc_endpoint": &hcldec.AttrSpec{Name: "vpc_endpoint", Type: cty.String, Required: false}, + "tag_endpoint": &hcldec.AttrSpec{Name: "tag_endpoint", Type: cty.String, Required: false}, + "security_token": &hcldec.AttrSpec{Name: "security_token", Type: cty.String, Required: false}, + "assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*cvm.FlatTencentCloudAccessRole)(nil).HCL2Spec())}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "shared_credentials_dir": &hcldec.AttrSpec{Name: "shared_credentials_dir", Type: cty.String, Required: false}, + "filters": &hcldec.AttrSpec{Name: "filters", Type: cty.Map(cty.String), Required: false}, + "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, + "most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatDatasourceOutput struct { + ID *string `mapstructure:"id" cty:"id" hcl:"id"` + Name *string `mapstructure:"name" cty:"name" hcl:"name"` +} + +// FlatMapstructure returns a new FlatDatasourceOutput. +// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatDatasourceOutput) +} + +// HCL2Spec returns the hcl spec of a DatasourceOutput. +// This spec is used by HCL to read the fields of DatasourceOutput. +// The decoded values from this spec will then be applied to a FlatDatasourceOutput. +func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false}, + "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, + } + return s +} diff --git a/datasource/tencentcloud/image/data_acc_test.go b/datasource/tencentcloud/image/data_acc_test.go new file mode 100644 index 0000000..58c1b6d --- /dev/null +++ b/datasource/tencentcloud/image/data_acc_test.go @@ -0,0 +1,66 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package image + +import ( + _ "embed" + "fmt" + "io/ioutil" + "os" + "os/exec" + "regexp" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/acctest" +) + +//go:embed test-fixtures/image.pkr.hcl +var testImageDatasource string + +// Run with: PACKER_ACC=1 go test -count 1 -v ./datasource/image/data_acc_test.go -timeout=120m +func TestAccImageDatasource(t *testing.T) { + testCase := &acctest.PluginTestCase{ + Name: "image_datasource_basic_test", + Setup: func() error { + return nil + }, + Teardown: func() error { + return nil + }, + Template: testImageDatasource, + Type: "image-my-datasource", + Check: func(buildCommand *exec.Cmd, logfile string) error { + if buildCommand.ProcessState != nil { + if buildCommand.ProcessState.ExitCode() != 0 { + return fmt.Errorf("Bad exit code. Logfile: %s", logfile) + } + } + + logs, err := os.Open(logfile) + if err != nil { + return fmt.Errorf("Unable find %s", logfile) + } + defer logs.Close() + + logsBytes, err := ioutil.ReadAll(logs) + if err != nil { + return fmt.Errorf("Unable to read %s", logfile) + } + logsString := string(logsBytes) + + idLog := "null.basic-example: id: img-kvtbik4g" + nameLog := "null.basic-example: name: image-family-test" + + if matched, _ := regexp.MatchString(idLog+".*", logsString); !matched { + t.Fatalf("logs doesn't contain expected ID value %q", logsString) + } + if matched, _ := regexp.MatchString(nameLog+".*", logsString); !matched { + t.Fatalf("logs doesn't contain expected name value %q", logsString) + } + + return nil + }, + } + acctest.TestPlugin(t, testCase) +} diff --git a/datasource/tencentcloud/image/data_test.go b/datasource/tencentcloud/image/data_test.go new file mode 100644 index 0000000..e1a90d5 --- /dev/null +++ b/datasource/tencentcloud/image/data_test.go @@ -0,0 +1,89 @@ +package image + +import ( + "testing" + + cvm "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm" +) + +var tencentCloudAccessConfig = cvm.TencentCloudAccessConfig{ + Region: "na-ashburn", + SecretId: "secret", + SecretKey: "key", +} + +func TestDatasourceConfigure_NoOptionsSpecified(t *testing.T) { + ds := Datasource{ + config: Config{ + TencentCloudAccessConfig: tencentCloudAccessConfig, + ImageFilterOptions: ImageFilterOptions{ + Filters: map[string]string{}, + ImageFamily: "", + MostRecent: false, + }, + }, + } + + if err := ds.Configure(); err == nil { + t.Fatal("Should fail since at least one option must be specified") + } else { + t.Log(err) + } +} + +func TestDatasourceConfigure_BothFiltersAndImageFamilySpecified(t *testing.T) { + ds := Datasource{ + config: Config{ + TencentCloudAccessConfig: tencentCloudAccessConfig, + ImageFilterOptions: ImageFilterOptions{ + Filters: map[string]string{ + "foo": "bar", + }, + ImageFamily: "foo", + MostRecent: false, + }, + }, + } + + if err := ds.Configure(); err == nil { + t.Fatal("Should fail since options are mutually exclusive") + } else { + t.Log(err) + } +} + +func TestDatasourceConfigure_FiltersSpecified(t *testing.T) { + ds := Datasource{ + config: Config{ + TencentCloudAccessConfig: tencentCloudAccessConfig, + ImageFilterOptions: ImageFilterOptions{ + Filters: map[string]string{ + "foo": "bar", + }, + ImageFamily: "", + MostRecent: false, + }, + }, + } + + if err := ds.Configure(); err != nil { + t.Fatal("Should not fail") + } +} + +func TestDatasourceConfigure_ImageFamilySpecified(t *testing.T) { + ds := Datasource{ + config: Config{ + TencentCloudAccessConfig: tencentCloudAccessConfig, + ImageFilterOptions: ImageFilterOptions{ + Filters: map[string]string{}, + ImageFamily: "foo", + MostRecent: false, + }, + }, + } + + if err := ds.Configure(); err != nil { + t.Fatal("Should not fail") + } +} diff --git a/datasource/tencentcloud/image/sorting.go b/datasource/tencentcloud/image/sorting.go new file mode 100644 index 0000000..7a6824c --- /dev/null +++ b/datasource/tencentcloud/image/sorting.go @@ -0,0 +1,30 @@ +package image + +import ( + "sort" + "time" + + cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" +) + +type imageSort []*cvm.Image + +func (a imageSort) Len() int { return len(a) } +func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a imageSort) Less(i, j int) bool { + // Public images don't have a creation time + if a[i].CreatedTime == nil || a[j].CreatedTime == nil { + return false + } + + itime, _ := time.Parse(time.RFC3339, *a[i].CreatedTime) + jtime, _ := time.Parse(time.RFC3339, *a[j].CreatedTime) + return itime.Before(jtime) +} + +func mostRecentImage(images []*cvm.Image) *cvm.Image { + sortedImages := images + sort.Sort(imageSort(sortedImages)) + + return sortedImages[len(sortedImages)-1] +} diff --git a/datasource/tencentcloud/image/test-fixtures/image.pkr.hcl b/datasource/tencentcloud/image/test-fixtures/image.pkr.hcl new file mode 100644 index 0000000..09e876d --- /dev/null +++ b/datasource/tencentcloud/image/test-fixtures/image.pkr.hcl @@ -0,0 +1,30 @@ +data "tencentcloud-image" "test-image" { + filters = { + image-type = "PRIVATE_IMAGE" + image-name = "image-family-test" + } + most_recent = true + region = "ap-guangzhou" +} + +locals { + id = data.tencentcloud-image.test-image.id + name = data.tencentcloud-image.test-image.name +} + +source "null" "basic-example" { + communicator = "none" +} + +build { + sources = [ + "source.null.basic-example" + ] + + provisioner "shell-local" { + inline = [ + "echo id: ${local.id}", + "echo name: ${local.name}", + ] + } +} \ No newline at end of file diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx index 67405b8..7bb930b 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx @@ -6,6 +6,9 @@ - `vpc_endpoint` (string) - The endpoint you want to reach the cloud endpoint, if tce cloud you should set a tce vpc endpoint. +- `tag_endpoint` (string) - The endpoint you want to reach the cloud endpoint, + if tce cloud you should set a tce tag endpoint. + - `security_token` (string) - STS access token, can be set through template or by exporting as environment variable such as `export TENCENTCLOUD_SECURITY_TOKEN=value`. diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-required.mdx index 3f93b9a..80aff72 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-required.mdx @@ -10,8 +10,4 @@ reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) for parameter taking. -- `zone` (string) - The zone where your cvm will be launch. You should - reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) - for parameter taking. - diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index b1c3baf..21a382c 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -15,4 +15,8 @@ - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `snapshot_tags` (map[string]string) - Key/value pair tags that will be applied to snapshot. + +- `skip_create_image` (bool) - Skip creating an image. When set to true, you don't need to enter target image information, share, copy, etc. The default value is false. + diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-required.mdx index 7bc83eb..dd2eaef 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-required.mdx @@ -4,4 +4,8 @@ You should reference [Instance Type](https://intl.cloud.tencent.com/document/product/213/11518) for parameter taking. +- `zone` (string) - The zone where your cvm will be launch. You should + reference [Region and Zone](https://intl.cloud.tencent.com/document/product/213/6091) + for parameter taking. + diff --git a/go.mod b/go.mod index b5dc20d..6c5fad9 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,19 @@ require ( github.com/hashicorp/hcl/v2 v2.19.1 github.com/hashicorp/packer-plugin-sdk v0.5.2 github.com/pkg/errors v0.9.1 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1072 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1175 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.1072 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.797 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.1175 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.1072 github.com/zclconf/go-cty v1.13.3 ) +require ( + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect +) + require ( cloud.google.com/go v0.105.0 // indirect cloud.google.com/go/compute v1.12.1 // indirect @@ -36,7 +42,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.0 github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect github.com/googleapis/gax-go/v2 v2.6.0 // indirect github.com/hashicorp/consul/api v1.25.1 // indirect @@ -69,7 +75,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/iochan v1.0.0 // indirect diff --git a/go.sum b/go.sum index 2b44835..104dac7 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,15 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute/metadata v0.1.1 h1:/sxEbyrm6cw+XOUw1YxBHlatV71z4vpnmO7z2IZ0h3I= cloud.google.com/go/compute/metadata v0.1.1/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/iam v0.6.0 h1:nsqQC88kT5Iwlm4MeNGTpfMWddp6NB/UOLFTH6m1QfQ= cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew= cloud.google.com/go/longrunning v0.1.1 h1:y50CXG4j0+qvEukslYFBCrzaXX0qpFbBzc3PchSu/LE= cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= @@ -73,7 +68,6 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -124,10 +118,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= @@ -145,7 +137,6 @@ github.com/hashicorp/go-getter/s3/v2 v2.2.1/go.mod h1:KDqfEPgpwZIy+1sAplFX231CE+ github.com/hashicorp/go-getter/v2 v2.2.1 h1:2JXqPZs1Jej67RtdTi0YZaEB2hEFB3fkBA4cPYKQwFQ= github.com/hashicorp/go-getter/v2 v2.2.1/go.mod h1:EcJx6oZE8hmGuRR1l38QrfnyiujQbwsEAn11eHv6l2M= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -213,7 +204,6 @@ github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -260,12 +250,13 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nywilken/go-cty v1.13.3 h1:03U99oXf3j3g9xgqAE3YGpixCjM8Mg09KZ0Ji9LzX0o= @@ -307,65 +298,35 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.366/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.367 h1:wZpJtVV05zBriiyAMZtHF7wSgBFUdDiXdnzD/Ecj7Ds= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.367/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.624/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.779/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.797 h1:jDSfrthql0LxaWOkEoQk/bSYRvM/k2+ukjsE6VfStEQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.797/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.799 h1:jAMelFh7c+sBrR2kzdNB2zfmkhsEXLIR9YFQcBuTnzI= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.799/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1072 h1:zoo8LhsH0kC3ysBCMDmgOCVzyQKTpw7foOzNoxAXcGE= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1072/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.366 h1:NJm4RjeL2btX3alWLQvyzObmlDtGC0pCFCoeqWw2Veg= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.366/go.mod h1:x9QV7qu6FpnSdVyGQoirhjKsPd1dEpWnr9RL75DpgJ4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.624/go.mod h1:+TXSVyeKwt1IhZRqKPbTREteBcP+K07Q846/ilNzLWA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.799 h1:FnXNkHQhPX7sNvxKNYyMB6PGpbMCce6bfXkzRwGHS74= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.799/go.mod h1:bNuzbq27CiymhqONoqE1CnhK6aJJjWWcZG8J3ragVfs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1175 h1:w0z4dtrinCY3R4aHnw9vcq80XWEIEKXv6c3p779ueRo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1175/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/sts v1.0.797 h1:Z9rTZBoR4arEXA9gYLu8AQnMInG1scb+WnlIWczLH2A= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.797/go.mod h1:IugQh1ZI86ZeEUBYf+u/REwTeKZcneP449FPU8BbLxA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.366 h1:7wbTvCCJ41Hx9KWO9pcmvOFWFS1A9iPs0jtQJLwe++U= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.366/go.mod h1:TcIZ64TWquVpU7SmDHScoRUkx4P3Jm/lWq4BYs6IEN8= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.779 h1:4NpjQiFgnIH662ydP7AecllyrhH+CVoGlzQ9V7RfD08= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.779/go.mod h1:kYBG2jgpjL7CuhYM+K1fkEtbWvNXrtt7NSLwXVCqmKA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.799 h1:6M8TGTEvrLAjxaKl53RyDIktCmF8kPuL0swJeKsbR/E= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.799/go.mod h1:jq1PLPim6gB9soBqQ/H6fRAI/NYlj/Qtn8JZfOK+eWw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.1175 h1:Yw7azOzlQqh6/Zyk5JCLH69LJ7nE2jkilGXU+3vIqUM= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.1175/go.mod h1:8CNgZPCw4+fKMA/cAsAcdkQUdz6B7xicnFtsLxJWkOI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.1072 h1:qG5L/VZcw4PC+OSMjxM1BNXlOfJ9vvDfToItgmCxfLE= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.1072/go.mod h1:fynFW8ciFiCFLVyIKyQKENQ6g+Tm7mXw687lFgPmU/Y= -github.com/tencentcloudstack/terraform-provider-tencentcloud v1.81.47 h1:6e9miVImuKP6g2HDDyNNTGrh/UnCmgkqJ+CInE6kZ7s= -github.com/tencentcloudstack/terraform-provider-tencentcloud v1.81.47/go.mod h1:YuYFm1AJbAELr9L799QJdLkZVkBaeFHokCeBG4J02Lc= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/yangwenmai/ratelimit v0.0.0-20180104140304-44221c2292e1 h1:q6c//IMJug6THoqsseZ+Z/zq53HQvADPh5a66E9hb+I= -github.com/yangwenmai/ratelimit v0.0.0-20180104140304-44221c2292e1/go.mod h1:Rl8MvKI/yVRGN91gMEZAIf/92QtFQBSG/QFRHWQZtmo= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -373,9 +334,6 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 h1:O8uGbHCqlTp2P6QJSLmCojM4mN6UemYv8K+dCnmHmu0= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -403,7 +361,6 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -464,7 +421,6 @@ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3j golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.101.0 h1:lJPPeEBIRxGpGLwnBTam1NPEM8Z2BmmXEd3z812pjwM= google.golang.org/api v0.101.0/go.mod h1:CjxAAWWt3A3VrUE2IGDY2bgK5qhoG/OkyWVlYcP05MY= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -474,7 +430,6 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c h1:QgY/XxIAIeccR+Ca/rDdKubLIU9rcJ3xfy1DC/Wd2Oo= google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -482,7 +437,6 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -496,13 +450,12 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -510,7 +463,6 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 212f8fc..3267a4e 100644 --- a/main.go +++ b/main.go @@ -10,12 +10,14 @@ import ( "github.com/hashicorp/packer-plugin-sdk/plugin" "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm" + "github.com/hashicorp/packer-plugin-tencentcloud/datasource/tencentcloud/image" "github.com/hashicorp/packer-plugin-tencentcloud/version" ) func main() { pps := plugin.NewSet() pps.RegisterBuilder("cvm", new(cvm.Builder)) + pps.RegisterDatasource("image", new(image.Datasource)) pps.SetVersion(version.PluginVersion) err := pps.Run() if err != nil {