From 6cefc8ffcb670d3d670b1a896cd4aa4949307774 Mon Sep 17 00:00:00 2001 From: hellertang Date: Wed, 14 Aug 2024 17:47:47 +0800 Subject: [PATCH 01/36] add more disk type --- .web-docs/components/builder/cvm/README.md | 2 +- builder/tencentcloud/cvm/run_config.go | 4 ++-- .../tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 227bbb61..798abdc3 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -134,7 +134,7 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can type for all data disks, and each data disk size will use the origin value in source image. The data disks allow for the following argument: - - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM` and `CLOUD_SSD`. + - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM`, `CLOUD_SSD`, `CLOUD_BSSD`, `CLOUD_HSSD` and `CLOUD_TSSD`. - `disk_size` - Size of the data disk. - `disk_snapshot_id` - Id of the snapshot for a data disk. diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index e4a45e11..753d7961 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -57,7 +57,7 @@ type TencentCloudRunConfig struct { // type for all data disks, and each data disk size will use the origin // value in source image. // The data disks allow for the following argument: - // - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM` and `CLOUD_SSD`. + // - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM`, `CLOUD_SSD`, `CLOUD_BSSD`, `CLOUD_HSSD` and `CLOUD_TSSD`. // - `disk_size` - Size of the data disk. // - `disk_snapshot_id` - Id of the snapshot for a data disk. DataDisks []tencentCloudDataDisk `mapstructure:"data_disks"` @@ -110,7 +110,7 @@ type TencentCloudRunConfig struct { } var ValidCBSType = []string{ - "LOCAL_BASIC", "LOCAL_SSD", "CLOUD_BASIC", "CLOUD_SSD", "CLOUD_PREMIUM", + "LOCAL_BASIC", "LOCAL_SSD", "CLOUD_BASIC", "CLOUD_PREMIUM", "CLOUD_BSSD", "CLOUD_SSD", "CLOUD_HSSD", "CLOUD_TSSD", } func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx index 7d1fabb9..c17d6aed 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx @@ -28,7 +28,7 @@ type for all data disks, and each data disk size will use the origin value in source image. The data disks allow for the following argument: - - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM` and `CLOUD_SSD`. + - `disk_type` - Type of the data disk. Valid choices: `CLOUD_BASIC`, `CLOUD_PREMIUM`, `CLOUD_SSD`, `CLOUD_BSSD`, `CLOUD_HSSD` and `CLOUD_TSSD`. - `disk_size` - Size of the data disk. - `disk_snapshot_id` - Id of the snapshot for a data disk. From adef8db78efaa7a9ca48c64db981031b0713450d Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Tue, 22 Oct 2024 13:09:58 +0300 Subject: [PATCH 02/36] Log the instance ID as soon as it's been created --- builder/tencentcloud/cvm/step_run_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index 943eabf6..aee81854 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -173,7 +173,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 { From 96a98f9cc9b44c7e5a142da556224ac91f1ba234 Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Tue, 22 Oct 2024 13:10:05 +0300 Subject: [PATCH 03/36] Use a unique value as ClientToken for each RunInstances request The purpose of ClientToken is to ensure request idempotency in case the request needs to be retried. This means the value must be unique for all unique RunInstances API calls. When building a template that references the same source multiple times (or multiple different sources), many calls to RunInstances are made. However, the same auto-generated instance name was used as "ClientToken". This changes ClientToken to be unique regardless of whether the user has supplied a unique "instance_name" for each source/build. Fixes https://github.com/hashicorp/packer-plugin-tencentcloud/issues/6 --- builder/tencentcloud/cvm/step_run_instance.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index aee81854..f79afefb 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" ) @@ -125,7 +126,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 @@ -135,7 +140,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 From 9da8cc80042f392c511913a0502f0ff505658ddf Mon Sep 17 00:00:00 2001 From: arunma Date: Mon, 6 Jan 2025 19:13:05 +0800 Subject: [PATCH 04/36] feat: support cdc --- builder/tencentcloud/cvm/builder.go | 2 ++ builder/tencentcloud/cvm/run_config.go | 2 ++ builder/tencentcloud/cvm/step_config_subnet.go | 2 ++ builder/tencentcloud/cvm/step_run_instance.go | 6 ++++++ go.mod | 6 +++--- go.sum | 10 ++++++++-- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index cda74a0c..55f44834 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -109,6 +109,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) SubnetCidrBlock: b.config.SubnectCidrBlock, SubnetName: b.config.SubnetName, Zone: b.config.Zone, + CdcId: b.config.CdcId, }, &stepConfigSecurityGroup{ SecurityGroupId: b.config.SecurityGroupId, @@ -132,6 +133,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) AssociatePublicIpAddress: b.config.AssociatePublicIpAddress, CamRoleName: b.config.CamRoleName, Tags: b.config.RunTags, + CdcId: b.config.CdcId, }, &communicator.StepConnect{ Config: &b.config.TencentCloudRunConfig.Comm, diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index e4a45e11..3634650a 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -103,6 +103,8 @@ type TencentCloudRunConfig struct { // [`dynamic_block`](/packer/docs/templates/hcl_templates/expressions#dynamic-blocks) // will allow you to create those programatically. RunTag config.KeyValues `mapstructure:"run_tag" required:"false"` + // Support for local dedicated cluster CDC + CdcId string `mapstructure:"cdc_id" required:"false"` // Communicator settings Comm communicator.Config `mapstructure:",squash"` diff --git a/builder/tencentcloud/cvm/step_config_subnet.go b/builder/tencentcloud/cvm/step_config_subnet.go index 085ada76..b93eeeb0 100644 --- a/builder/tencentcloud/cvm/step_config_subnet.go +++ b/builder/tencentcloud/cvm/step_config_subnet.go @@ -17,6 +17,7 @@ type stepConfigSubnet struct { SubnetName string Zone string isCreate bool + CdcId string } func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -57,6 +58,7 @@ func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) mu req.SubnetName = &s.SubnetName req.CidrBlock = &s.SubnetCidrBlock req.Zone = &s.Zone + req.CdcId = &s.CdcId var resp *vpc.CreateSubnetResponse err := Retry(ctx, func(ctx context.Context) error { var e error diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index 943eabf6..774a76c3 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -32,6 +32,7 @@ type stepRunInstance struct { AssociatePublicIpAddress bool Tags map[string]string DataDisks []tencentCloudDataDisk + CdcId string } func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -66,9 +67,13 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul if instanceChargeType == "" { instanceChargeType = "POSTPAID_BY_HOUR" } + if s.CdcId != "" { + instanceChargeType = "CDCPAID" + } req.InstanceChargeType = &instanceChargeType req.ImageId = source_image.ImageId req.InstanceType = &s.InstanceType + req.DedicatedClusterId = &s.CdcId // TODO: Add check for system disk size, it should be larger than image system disk size. req.SystemDisk = &cvm.SystemDisk{ DiskType: &s.DiskType, @@ -157,6 +162,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul }, } } + Message(state, req.ToJsonString(), "[DEBUG] request data") var resp *cvm.RunInstancesResponse err = Retry(ctx, func(ctx context.Context) error { diff --git a/go.mod b/go.mod index 4004da58..b5dc20d6 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ 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.799 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.799 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1072 + 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/vpc v1.0.799 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.1072 github.com/zclconf/go-cty v1.13.3 ) diff --git a/go.sum b/go.sum index 2d636fbc..2b448358 100644 --- a/go.sum +++ b/go.sum @@ -145,9 +145,9 @@ 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-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -329,11 +329,15 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.797 h1:jDSf 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/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= @@ -342,6 +346,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.779 h1:4NpjQiF 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/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= @@ -350,12 +356,12 @@ 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/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= 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= From 1a9ea3656d92b70e2ba9c0ec6a7ba02e8746c1e4 Mon Sep 17 00:00:00 2001 From: arunma Date: Wed, 8 Jan 2025 10:52:39 +0800 Subject: [PATCH 05/36] fix: modify print --- builder/tencentcloud/cvm/step_run_instance.go | 1 - 1 file changed, 1 deletion(-) diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index 774a76c3..183d54e5 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -162,7 +162,6 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul }, } } - Message(state, req.ToJsonString(), "[DEBUG] request data") var resp *cvm.RunInstancesResponse err = Retry(ctx, func(ctx context.Context) error { From 4cc9db29bfe5965081d16db2eeb99fc6c2c8b80b Mon Sep 17 00:00:00 2001 From: arunma Date: Mon, 20 Jan 2025 21:03:21 +0800 Subject: [PATCH 06/36] fix: generate doc --- .web-docs/components/builder/cvm/README.md | 2 ++ builder/tencentcloud/cvm/builder.hcl2spec.go | 2 ++ .../tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 227bbb61..be2299bf 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -180,6 +180,8 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can [`dynamic_block`](/packer/docs/templates/hcl_templates/expressions#dynamic-blocks) will allow you to create those programatically. +- `cdc_id` (string) - Support for local dedicated cluster CDC + - `ssh_private_ip` (bool) - SSH Private Ip diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 51eadf70..13988ba1 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -62,6 +62,7 @@ type FlatConfig struct { CamRoleName *string `mapstructure:"cam_role_name" required:"false" cty:"cam_role_name" hcl:"cam_role_name"` RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags" hcl:"run_tags"` RunTag []config.FlatKeyValue `mapstructure:"run_tag" required:"false" cty:"run_tag" hcl:"run_tag"` + CdcId *string `mapstructure:"cdc_id" required:"false" cty:"cdc_id" hcl:"cdc_id"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` @@ -178,6 +179,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "cam_role_name": &hcldec.AttrSpec{Name: "cam_role_name", Type: cty.String, Required: false}, "run_tags": &hcldec.AttrSpec{Name: "run_tags", Type: cty.Map(cty.String), Required: false}, "run_tag": &hcldec.BlockListSpec{TypeName: "run_tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())}, + "cdc_id": &hcldec.AttrSpec{Name: "cdc_id", Type: cty.String, Required: false}, "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", 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 7d1fabb9..1e5b5f52 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-not-required.mdx @@ -74,6 +74,8 @@ [`dynamic_block`](/packer/docs/templates/hcl_templates/expressions#dynamic-blocks) will allow you to create those programatically. +- `cdc_id` (string) - Support for local dedicated cluster CDC + - `ssh_private_ip` (bool) - SSH Private Ip From 893785b46199d146e3b9095fdabcb55031f24d2e Mon Sep 17 00:00:00 2001 From: arunma Date: Thu, 6 Feb 2025 17:58:33 +0800 Subject: [PATCH 07/36] fix: modify cdcid --- builder/tencentcloud/cvm/step_run_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index 183d54e5..df2afd03 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -69,11 +69,11 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul } if s.CdcId != "" { instanceChargeType = "CDCPAID" + req.DedicatedClusterId = &s.CdcId } req.InstanceChargeType = &instanceChargeType req.ImageId = source_image.ImageId req.InstanceType = &s.InstanceType - req.DedicatedClusterId = &s.CdcId // TODO: Add check for system disk size, it should be larger than image system disk size. req.SystemDisk = &cvm.SystemDisk{ DiskType: &s.DiskType, From 7c5b644efd440ca5d04af65d2146dfa9bf16ab9c Mon Sep 17 00:00:00 2001 From: arunma Date: Wed, 12 Feb 2025 16:03:55 +0800 Subject: [PATCH 08/36] fix: Optimize subnet creation --- builder/tencentcloud/cvm/step_config_subnet.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/step_config_subnet.go b/builder/tencentcloud/cvm/step_config_subnet.go index b93eeeb0..855512f4 100644 --- a/builder/tencentcloud/cvm/step_config_subnet.go +++ b/builder/tencentcloud/cvm/step_config_subnet.go @@ -58,7 +58,9 @@ func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) mu req.SubnetName = &s.SubnetName req.CidrBlock = &s.SubnetCidrBlock req.Zone = &s.Zone - req.CdcId = &s.CdcId + if s.CdcId != "" { + req.CdcId = &s.CdcId + } var resp *vpc.CreateSubnetResponse err := Retry(ctx, func(ctx context.Context) error { var e error From 992d4a8a1f081f1ab7686a5f31f5a76a8bea3baa Mon Sep 17 00:00:00 2001 From: Arunma <41282634+gitmkn@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:35:49 +0800 Subject: [PATCH 09/36] Create sync-code.yaml add sync-code --- .github/workflows/sync-code.yaml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/sync-code.yaml diff --git a/.github/workflows/sync-code.yaml b/.github/workflows/sync-code.yaml new file mode 100644 index 00000000..63cbd4e5 --- /dev/null +++ b/.github/workflows/sync-code.yaml @@ -0,0 +1,31 @@ +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 From f488291e77a4d56ea6697496081a68db180efd59 Mon Sep 17 00:00:00 2001 From: Arunma <41282634+gitmkn@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:07:07 +0800 Subject: [PATCH 10/36] Update notify-integration-release-via-tag.yaml --- .../notify-integration-release-via-tag.yaml | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/.github/workflows/notify-integration-release-via-tag.yaml b/.github/workflows/notify-integration-release-via-tag.yaml index a99a2aef..9c9a17c6 100644 --- a/.github/workflows/notify-integration-release-via-tag.yaml +++ b/.github/workflows/notify-integration-release-via-tag.yaml @@ -1,55 +1,55 @@ -name: Notify Integration Release (Tag) -on: - push: - tags: - - '*.*.*' # Proper releases -jobs: - strip-version: - runs-on: ubuntu-latest - outputs: - packer-version: ${{ steps.strip.outputs.packer-version }} - steps: - - name: Strip leading v from version tag - id: strip - env: - REF: ${{ github.ref_name }} - run: | - echo "packer-version=$(echo "$REF" | sed -E 's/v?([0-9]+\.[0-9]+\.[0-9]+)/\1/')" >> "$GITHUB_OUTPUT" - notify-release: - needs: - - strip-version - runs-on: ubuntu-latest - steps: - - name: Checkout this repo - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - with: - ref: ${{ github.ref }} - # Ensure that Docs are Compiled - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - - shell: bash - run: make generate - - shell: bash - run: | - uncommitted="$(git status -s)" - if [[ -z "$uncommitted" ]]; then - echo "OK" - else - echo "Docs have been updated, but the compiled docs have not been committed." - echo "Run 'make generate', and commit the result to resolve this error." - echo "Generated but uncommitted files:" - echo "$uncommitted" - exit 1 - fi - # Perform the Release - - name: Checkout integration-release-action - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - with: - repository: hashicorp/integration-release-action - path: ./integration-release-action - - name: Notify Release - uses: ./integration-release-action - with: - integration_identifier: "packer/hashicorp/tencentcloud" - release_version: ${{ needs.strip-version.outputs.packer-version }} - release_sha: ${{ github.ref }} - github_token: ${{ secrets.GITHUB_TOKEN }} +# name: Notify Integration Release (Tag) +# on: +# push: +# tags: +# - '*.*.*' # Proper releases +# jobs: +# strip-version: +# runs-on: ubuntu-latest +# outputs: +# packer-version: ${{ steps.strip.outputs.packer-version }} +# steps: +# - name: Strip leading v from version tag +# id: strip +# env: +# REF: ${{ github.ref_name }} +# run: | +# echo "packer-version=$(echo "$REF" | sed -E 's/v?([0-9]+\.[0-9]+\.[0-9]+)/\1/')" >> "$GITHUB_OUTPUT" +# notify-release: +# needs: +# - strip-version +# runs-on: ubuntu-latest +# steps: +# - name: Checkout this repo +# uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 +# with: +# ref: ${{ github.ref }} +# # Ensure that Docs are Compiled +# - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 +# - shell: bash +# run: make generate +# - shell: bash +# run: | +# uncommitted="$(git status -s)" +# if [[ -z "$uncommitted" ]]; then +# echo "OK" +# else +# echo "Docs have been updated, but the compiled docs have not been committed." +# echo "Run 'make generate', and commit the result to resolve this error." +# echo "Generated but uncommitted files:" +# echo "$uncommitted" +# exit 1 +# fi +# # Perform the Release +# - name: Checkout integration-release-action +# uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 +# with: +# repository: hashicorp/integration-release-action +# path: ./integration-release-action +# - name: Notify Release +# uses: ./integration-release-action +# with: +# integration_identifier: "packer/hashicorp/tencentcloud" +# release_version: ${{ needs.strip-version.outputs.packer-version }} +# release_sha: ${{ github.ref }} +# github_token: ${{ secrets.GITHUB_TOKEN }} From 1e6f98315a475bd8c1c493f5643e230e90c5d72f Mon Sep 17 00:00:00 2001 From: Arunma <41282634+gitmkn@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:07:42 +0800 Subject: [PATCH 11/36] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec35d84b..813ec34f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,8 +48,8 @@ jobs: - name: Describe plugin id: plugin_describe run: echo "api_version=$(go run . describe | jq -r '.api_version')" >> "$GITHUB_OUTPUT" - - name: Install signore - uses: hashicorp/setup-signore-package@v1 + # - name: Install signore + # uses: hashicorp/setup-signore-package@v1 - name: Run GoReleaser uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 with: From 3d3df33c5418702cb18223577783d408eff083f0 Mon Sep 17 00:00:00 2001 From: arunma Date: Thu, 13 Mar 2025 20:27:34 +0800 Subject: [PATCH 12/36] feat: support tccli oauth --- builder/tencentcloud/cvm/access_config.go | 144 ++-------------- builder/tencentcloud/cvm/oauth.go | 191 ++++++++++++++++++++++ builder/tencentcloud/cvm/profile.go | 139 ++++++++++++++++ 3 files changed, 341 insertions(+), 133 deletions(-) create mode 100644 builder/tencentcloud/cvm/oauth.go create mode 100644 builder/tencentcloud/cvm/profile.go diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go index 66a376d9..89545399 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -8,16 +8,11 @@ package cvm import ( "context" - "encoding/json" "fmt" - "io/ioutil" "os" - "runtime" "strconv" - "strings" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - "github.com/mitchellh/go-homedir" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" ) @@ -222,35 +217,23 @@ func (cf *TencentCloudAccessConfig) Config() error { cf.SharedCredentialsDir = os.Getenv(PACKER_SHARED_CREDENTIALS_DIR) } - var value map[string]interface{} - var err error - getProviderConfig := func(key string) string { - var str string - if value != nil { - if v, ok := value[key]; ok { - str = v.(string) - } - } - return str - } - if cf.SecretId == "" || cf.SecretKey == "" { - value, err = loadConfigProfile(cf) + profile, err := loadConfigProfile(cf) if err != nil { return err } if cf.SecretId == "" { - cf.SecretId = getProviderConfig("secretId") + cf.SecretId = profile.SecretId } if cf.SecretKey == "" { - cf.SecretKey = getProviderConfig("secretKey") + cf.SecretKey = profile.SecretKey } if cf.SecurityToken == "" { - cf.SecurityToken = getProviderConfig("securityToken") + cf.SecurityToken = profile.Token } if cf.Region == "" { - cf.Region = getProviderConfig("region") + cf.Region = profile.Region } } @@ -293,33 +276,29 @@ func (cf *TencentCloudAccessConfig) Config() error { } if cf.AssumeRole.RoleArn == "" || cf.AssumeRole.SessionName == "" { - value, err = loadConfigProfile(cf) + profile, err := loadConfigProfile(cf) if err != nil { return err } if cf.AssumeRole.RoleArn == "" { - roleArn := getProviderConfig("role-arn") + roleArn := profile.RoleArn if roleArn != "" { cf.AssumeRole.RoleArn = roleArn } } if cf.AssumeRole.SessionName == "" { - sessionName := getProviderConfig("role-session-name") + sessionName := profile.RoleSessionName if sessionName != "" { cf.AssumeRole.SessionName = sessionName } } if cf.AssumeRole.SessionDuration == 0 { - duration := getProviderConfig("role-session-duration") - if duration != "" { - durationInt, err := strconv.Atoi(duration) - if err != nil { - return err - } - cf.AssumeRole.SessionDuration = durationInt + duration := profile.RoleSessionDuration + if duration != 0 { + cf.AssumeRole.SessionDuration = int(duration) } } } @@ -344,104 +323,3 @@ func validRegion(region string) error { return fmt.Errorf("unknown region: %s", region) } - -func getProfilePatch(cf *TencentCloudAccessConfig) (string, string, error) { - var ( - profile string - sharedCredentialsDir string - credentialPath string - configurePath string - ) - - if cf.Profile != "" { - profile = cf.Profile - } else { - profile = DEFAULT_PROFILE - } - - if cf.SharedCredentialsDir != "" { - sharedCredentialsDir = cf.SharedCredentialsDir - } - - tmpSharedCredentialsDir, err := homedir.Expand(sharedCredentialsDir) - if err != nil { - return "", "", err - } - - if tmpSharedCredentialsDir == "" { - credentialPath = fmt.Sprintf("%s/.tccli/%s.credential", os.Getenv("HOME"), profile) - configurePath = fmt.Sprintf("%s/.tccli/%s.configure", os.Getenv("HOME"), profile) - if runtime.GOOS == "windows" { - credentialPath = fmt.Sprintf("%s/.tccli/%s.credential", os.Getenv("USERPROFILE"), profile) - configurePath = fmt.Sprintf("%s/.tccli/%s.configure", os.Getenv("USERPROFILE"), profile) - } - } else { - credentialPath = fmt.Sprintf("%s/%s.credential", tmpSharedCredentialsDir, profile) - configurePath = fmt.Sprintf("%s/%s.configure", tmpSharedCredentialsDir, profile) - } - - return credentialPath, configurePath, nil -} - -func loadConfigProfile(cf *TencentCloudAccessConfig) (map[string]interface{}, error) { - var ( - credentialPath string - configurePath string - ) - - credentialPath, configurePath, err := getProfilePatch(cf) - if err != nil { - return nil, err - } - - packerConfig := make(map[string]interface{}) - _, err = os.Stat(credentialPath) - if !os.IsNotExist(err) { - data, err := ioutil.ReadFile(credentialPath) - if err != nil { - return nil, err - } - - config := map[string]interface{}{} - err = json.Unmarshal(data, &config) - if err != nil { - return nil, fmt.Errorf("credential file unmarshal failed, %s", err) - } - - for k, v := range config { - packerConfig[k] = strings.TrimSpace(v.(string)) - } - } else { - return nil, fmt.Errorf("please set a valid secret_id and secret_key or shared_credentials_dir, %s", err) - } - _, err = os.Stat(configurePath) - if !os.IsNotExist(err) { - data, err := ioutil.ReadFile(configurePath) - if err != nil { - return nil, err - } - - config := map[string]interface{}{} - err = json.Unmarshal(data, &config) - if err != nil { - return nil, fmt.Errorf("configure file unmarshal failed, %s", err) - } - - outerLoop: - for k, v := range config { - if k == "_sys_param" { - tmpMap := v.(map[string]interface{}) - for tmpK, tmpV := range tmpMap { - if tmpK == "region" { - packerConfig[tmpK] = strings.TrimSpace(tmpV.(string)) - break outerLoop - } - } - } - } - } else { - return nil, fmt.Errorf("please set a valid region or shared_credentials_dir, %s", err) - } - - return packerConfig, nil -} diff --git a/builder/tencentcloud/cvm/oauth.go b/builder/tencentcloud/cvm/oauth.go new file mode 100644 index 00000000..6deda567 --- /dev/null +++ b/builder/tencentcloud/cvm/oauth.go @@ -0,0 +1,191 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cvm + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/google/uuid" +) + +const _API_ENDPOINT = "https://cli.cloud.tencent.com" + +func GetOauthConfig(p *Profile) error { + if p.Oauth == nil || p.Oauth.RefreshToken == "" || p.Oauth.OpenId == "" { + return fmt.Errorf("Oauth authentication information is not configured correctly") + } + client := NewAPIClient() + + expired := false + if p.Oauth.ExpiresAt != 0 { + now := time.Now() + futureTime := now.Add(30 * time.Second) + targetTime := time.Unix(p.Oauth.ExpiresAt, 0) + if targetTime.After(futureTime) { + expired = true + } + } + + if expired { + response, err := client.RefreshUserToken(p.Oauth.RefreshToken, p.Oauth.OpenId, p.Oauth.Site) + if err != nil { + return err + } + if response != nil { + if response.AccessToken != "" { + p.Oauth.AccessToken = response.AccessToken + } + if response.ExpiresAt != 0 { + p.Oauth.ExpiresAt = response.ExpiresAt + } + } + } + + // 获取临时token + response, err := client.GetThirdPartyFederationToken(p.Oauth.AccessToken, p.Oauth.Site) + if err != nil { + return err + } + if response != nil { + if response.SecretId != "" { + p.SecretId = response.SecretId + } + if response.SecretKey != "" { + p.SecretKey = response.SecretKey + } + if response.Token != "" { + p.Token = response.Token + } + } + + return nil +} + +type APIClient struct { + Client *http.Client +} + +// 创建新的APIClient +func NewAPIClient() *APIClient { + return &APIClient{ + Client: &http.Client{ + Timeout: 10 * time.Second, + }, + } +} + +// GetThirdPartyFederationToken Obtaining a temporary user certificate +func (c *APIClient) GetThirdPartyFederationToken(accessToken, site string) (*GetTempCredResponse, error) { + apiEndpoint := _API_ENDPOINT + "/get_temp_cred" + traceId := uuid.New().String() + + body := GetTempCredRequest{ + TraceId: traceId, + AccessToken: accessToken, + Site: site, + } + + jsonData, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("failed to marshal request body: %v", err) + } + + req, err := http.NewRequest("POST", apiEndpoint, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := c.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + response := &GetTempCredResponse{} + if err := json.NewDecoder(resp.Body).Decode(response); err != nil { + return nil, fmt.Errorf("failed to decode response body: %v", err) + } + + if response.Error != "" { + return nil, fmt.Errorf("get_temp_cred: %s", response.Error) + } + + return response, err +} + +// RefreshUserToken Refresh user third-party access_token +func (c *APIClient) RefreshUserToken(refToken, openId, site string) (*RefreshTokenResponse, error) { + apiEndpoint := _API_ENDPOINT + "/refresh_user_token" + + traceId := uuid.New().String() + + body := RefreshTokenRequest{ + TraceId: traceId, + RefreshToken: refToken, + OpenId: openId, + Site: site, + } + + jsonData, err := json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("failed to marshal request body: %v", err) + } + + // 创建POST请求 + req, err := http.NewRequest("POST", apiEndpoint, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %v", err) + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := c.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + response := &RefreshTokenResponse{} + if err := json.NewDecoder(resp.Body).Decode(response); err != nil { + return nil, fmt.Errorf("failed to decode response body: %v", err) + } + + if response.Error != "" { + return nil, fmt.Errorf("refresh_user_token: %s", response.Error) + } + return response, nil +} + +type RefreshTokenRequest struct { + TraceId string `json:"TraceId"` + RefreshToken string `json:"RefreshToken"` + OpenId string `json:"OpenId"` + Site string `json:"Site"` +} + +type RefreshTokenResponse struct { + AccessToken string `json:"AccessToken"` + ExpiresAt int64 `json:"ExpiresAt"` + Error string `json:"Error"` +} + +type GetTempCredRequest struct { + TraceId string `json:"TraceId"` + AccessToken string `json:"AccessToken"` + Site string `json:"Site"` +} + +type GetTempCredResponse struct { + SecretId string `json:"SecretId"` + SecretKey string `json:"SecretKey"` + Token string `json:"Token"` + ExpiresAt int64 `json:"ExpiresAt"` + Error string `json:"Error"` +} diff --git a/builder/tencentcloud/cvm/profile.go b/builder/tencentcloud/cvm/profile.go new file mode 100644 index 00000000..004495ec --- /dev/null +++ b/builder/tencentcloud/cvm/profile.go @@ -0,0 +1,139 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cvm + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "runtime" + "strings" + + "github.com/mitchellh/go-homedir" +) + +type Profile struct { + Type string `json:"type,omitempty"` + Region string + SecretId string `json:" ,omitempty"` + SecretKey string `json:"secretKey,omitempty"` + Token string `json:"token,omitempty"` + ExpiresAt int64 `json:"expiresAt,omitempty"` + RoleArn string `json:"role-arn,omitempty"` + RoleSessionName string `json:"role-session-name,omitempty"` + RoleSessionDuration int64 `json:"role-session-duration,omitempty"` + Oauth *Oauth `json:"oauth,omitempty"` +} + +type Oauth struct { + OpenId string `json:"openId,omitempty"` + AccessToken string `json:"accessToken,omitempty"` + ExpiresAt int64 `json:"expiresAt,omitempty"` + RefreshToken string `json:"refreshToken,omitempty"` + Site string `json:"site,omitempty"` +} + +func getProfilePatch(cf *TencentCloudAccessConfig) (string, string, error) { + var ( + profile string + sharedCredentialsDir string + credentialPath string + configurePath string + ) + + if cf.Profile != "" { + profile = cf.Profile + } else { + profile = DEFAULT_PROFILE + } + + if cf.SharedCredentialsDir != "" { + sharedCredentialsDir = cf.SharedCredentialsDir + } + + tmpSharedCredentialsDir, err := homedir.Expand(sharedCredentialsDir) + if err != nil { + return "", "", err + } + + if tmpSharedCredentialsDir == "" { + credentialPath = fmt.Sprintf("%s/.tccli/%s.credential", os.Getenv("HOME"), profile) + configurePath = fmt.Sprintf("%s/.tccli/%s.configure", os.Getenv("HOME"), profile) + if runtime.GOOS == "windows" { + credentialPath = fmt.Sprintf("%s/.tccli/%s.credential", os.Getenv("USERPROFILE"), profile) + configurePath = fmt.Sprintf("%s/.tccli/%s.configure", os.Getenv("USERPROFILE"), profile) + } + } else { + credentialPath = fmt.Sprintf("%s/%s.credential", tmpSharedCredentialsDir, profile) + configurePath = fmt.Sprintf("%s/%s.configure", tmpSharedCredentialsDir, profile) + } + + return credentialPath, configurePath, nil +} + +func loadConfigProfile(cf *TencentCloudAccessConfig) (*Profile, error) { + var ( + credentialPath string + configurePath string + ) + + credentialPath, configurePath, err := getProfilePatch(cf) + if err != nil { + return nil, err + } + + tcProfile := &Profile{} + _, err = os.Stat(credentialPath) + if !os.IsNotExist(err) { + data, err := ioutil.ReadFile(credentialPath) + if err != nil { + return nil, err + } + + err = json.Unmarshal(data, tcProfile) + if err != nil { + return nil, fmt.Errorf("credential file unmarshal failed, %s", err) + } + + if tcProfile.Type == "oauth" { + err := GetOauthConfig(tcProfile) + if err != nil { + return nil, fmt.Errorf("getOauthConfig failed, %v", err) + } + } + } else { + return nil, fmt.Errorf("please set a valid secret_id and secret_key or shared_credentials_dir, %s", err) + } + _, err = os.Stat(configurePath) + if !os.IsNotExist(err) { + data, err := ioutil.ReadFile(configurePath) + if err != nil { + return nil, err + } + + config := map[string]interface{}{} + err = json.Unmarshal(data, &config) + if err != nil { + return nil, fmt.Errorf("configure file unmarshal failed, %s", err) + } + + outerLoop: + for k, v := range config { + if k == "_sys_param" { + tmpMap := v.(map[string]interface{}) + for tmpK, tmpV := range tmpMap { + if tmpK == "region" { + tcProfile.Region = strings.TrimSpace(tmpV.(string)) + break outerLoop + } + } + } + } + } else { + return nil, fmt.Errorf("please set a valid region or shared_credentials_dir, %s", err) + } + + return tcProfile, nil +} From eb27ac338042f78bfe8efdfcca2c65386fdd28c1 Mon Sep 17 00:00:00 2001 From: arunma Date: Thu, 13 Mar 2025 20:35:28 +0800 Subject: [PATCH 13/36] feat: support tccli oauth --- builder/tencentcloud/cvm/profile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/profile.go b/builder/tencentcloud/cvm/profile.go index 004495ec..ca37d5a1 100644 --- a/builder/tencentcloud/cvm/profile.go +++ b/builder/tencentcloud/cvm/profile.go @@ -17,7 +17,7 @@ import ( type Profile struct { Type string `json:"type,omitempty"` Region string - SecretId string `json:" ,omitempty"` + SecretId string `json:"secretId,omitempty"` SecretKey string `json:"secretKey,omitempty"` Token string `json:"token,omitempty"` ExpiresAt int64 `json:"expiresAt,omitempty"` From 1c0e85f97131e22eb5e65e5671f0ee0635a5f199 Mon Sep 17 00:00:00 2001 From: Arunma <41282634+gitmkn@users.noreply.github.com> Date: Fri, 14 Mar 2025 14:50:16 +0800 Subject: [PATCH 14/36] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 813ec34f..ec35d84b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,8 +48,8 @@ jobs: - name: Describe plugin id: plugin_describe run: echo "api_version=$(go run . describe | jq -r '.api_version')" >> "$GITHUB_OUTPUT" - # - name: Install signore - # uses: hashicorp/setup-signore-package@v1 + - name: Install signore + uses: hashicorp/setup-signore-package@v1 - name: Run GoReleaser uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 with: From 3ccd6e5d9a2ee0751d1b467aa384a7bfe0b2bba4 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 14 Mar 2025 15:23:48 +0800 Subject: [PATCH 15/36] fix: moidfy release --- .github/workflows/release.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ec35d84b..fc275d51 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@v4 - name: Unshallow run: git fetch --prune --unshallow - name: Set up Go @@ -48,16 +48,16 @@ jobs: - name: Describe plugin id: plugin_describe run: echo "api_version=$(go run . describe | jq -r '.api_version')" >> "$GITHUB_OUTPUT" - - name: Install signore - uses: hashicorp/setup-signore-package@v1 + # - name: Install signore + # uses: hashicorp/setup-signore-package@v1 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 + uses: goreleaser/goreleaser-action@v6 with: version: latest args: release --clean --timeout 120m env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} - SIGNORE_CLIENT_ID: ${{ secrets.SIGNORE_CLIENT_ID }} - SIGNORE_CLIENT_SECRET: ${{ secrets.SIGNORE_CLIENT_SECRET }} - SIGNORE_SIGNER: ${{ secrets.SIGNORE_SIGNER }} + # SIGNORE_CLIENT_ID: ${{ secrets.SIGNORE_CLIENT_ID }} + # SIGNORE_CLIENT_SECRET: ${{ secrets.SIGNORE_CLIENT_SECRET }} + # SIGNORE_SIGNER: ${{ secrets.SIGNORE_SIGNER }} From 9c4da33a1a824bc7315fa17907c1fe850f500686 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 14 Mar 2025 15:25:50 +0800 Subject: [PATCH 16/36] fix: moidfy release --- .github/workflows/release.yml | 2 +- .goreleaser.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc275d51..0e5870b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: - name: Unshallow run: git fetch --prune --unshallow - name: Set up Go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@v5 with: go-version: ${{ needs.get-go-version.outputs.go-version }} - name: Describe plugin diff --git a/.goreleaser.yml b/.goreleaser.yml index 4e2db89a..e5e53b50 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -93,11 +93,11 @@ archives: checksum: name_template: '{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS' algorithm: sha256 -signs: - - cmd: signore - args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"] - artifacts: checksum - signature: ${artifact}.sig +# signs: +# - cmd: signore +# args: ["sign", "--dearmor", "--file", "${artifact}", "--out", "${signature}"] +# artifacts: checksum +# signature: ${artifact}.sig changelog: use: github-native From 055b4743f007a58937a166d3f01bbf3084223876 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 14 Mar 2025 16:31:43 +0800 Subject: [PATCH 17/36] fix: Modify expiration time judgment --- builder/tencentcloud/cvm/oauth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/oauth.go b/builder/tencentcloud/cvm/oauth.go index 6deda567..cc140ffb 100644 --- a/builder/tencentcloud/cvm/oauth.go +++ b/builder/tencentcloud/cvm/oauth.go @@ -26,7 +26,7 @@ func GetOauthConfig(p *Profile) error { now := time.Now() futureTime := now.Add(30 * time.Second) targetTime := time.Unix(p.Oauth.ExpiresAt, 0) - if targetTime.After(futureTime) { + if futureTime.After(targetTime) { expired = true } } From a5919e4eb34b698a679bbbc93fe98a3b1a88cbea Mon Sep 17 00:00:00 2001 From: arunma Date: Thu, 24 Apr 2025 16:03:57 +0800 Subject: [PATCH 18/36] fix: Support SkipCreateImage field to skip creating an image --- .web-docs/components/builder/cvm/README.md | 2 ++ builder/tencentcloud/cvm/builder.go | 13 +++++++++---- builder/tencentcloud/cvm/builder.hcl2spec.go | 2 ++ builder/tencentcloud/cvm/image_config.go | 6 ++++++ builder/tencentcloud/cvm/step_copy_image.go | 16 ++++++++++------ builder/tencentcloud/cvm/step_create_image.go | 9 ++++++++- builder/tencentcloud/cvm/step_pre_validate.go | 6 ++++++ .../cvm/TencentCloudImageConfig-not-required.mdx | 2 ++ 8 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 227bbb61..97f04399 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -101,6 +101,8 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `skip_create_image` (bool) - Skip creating 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/builder.go b/builder/tencentcloud/cvm/builder.go index cda74a0c..8c59610e 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -90,7 +90,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) // Build the steps var steps []multistep.Step steps = []multistep.Step{ - &stepPreValidate{}, + &stepPreValidate{ + b.config.SkipCreateImage, + }, &stepCheckSourceImage{ b.config.SourceImageId, }, @@ -145,13 +147,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 51eadf70..770c338b 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -36,6 +36,7 @@ type FlatConfig struct { ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions" hcl:"image_copy_regions"` ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false" cty:"image_share_accounts" hcl:"image_share_accounts"` ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"` + SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"` AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address" hcl:"associate_public_ip_address"` SourceImageId *string `mapstructure:"source_image_id" required:"false" cty:"source_image_id" hcl:"source_image_id"` SourceImageName *string `mapstructure:"source_image_name" required:"false" cty:"source_image_name" hcl:"source_image_name"` @@ -152,6 +153,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_copy_regions": &hcldec.AttrSpec{Name: "image_copy_regions", Type: cty.List(cty.String), Required: false}, "image_share_accounts": &hcldec.AttrSpec{Name: "image_share_accounts", Type: cty.List(cty.String), Required: false}, "image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.Map(cty.String), Required: false}, + "skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/image_config.go b/builder/tencentcloud/cvm/image_config.go index 036c30dd..9b207c3c 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -33,11 +33,17 @@ type TencentCloudImageConfig struct { // Key/value pair tags that will be applied to the resulting image. ImageTags map[string]string `mapstructure:"image_tags" required:"false"` skipValidation bool + // Skip creating 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 { diff --git a/builder/tencentcloud/cvm/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go index bc481e87..096a1b0e 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)) } diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index b337fbeb..1913ed33 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -12,7 +12,8 @@ import ( ) type stepCreateImage struct { - imageId string + imageId string + SkipCreateImage bool } func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { @@ -21,6 +22,12 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul config := state.Get("config").(*Config) instance := state.Get("instance").(*cvm.Instance) + // Optionally skip this step + if s.SkipCreateImage { + Say(state, "Skipping image creation step", "") + return multistep.ActionContinue + } + Say(state, config.ImageName, "Trying to create a new image") req := cvm.NewCreateImageRequest() diff --git a/builder/tencentcloud/cvm/step_pre_validate.go b/builder/tencentcloud/cvm/step_pre_validate.go index 4f654e93..4365c4d2 100644 --- a/builder/tencentcloud/cvm/step_pre_validate.go +++ b/builder/tencentcloud/cvm/step_pre_validate.go @@ -12,9 +12,15 @@ import ( ) type stepPreValidate struct { + SkipCreateImage bool } func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + // No need to validate the image name if we're not creating an image + if s.SkipCreateImage { + return multistep.ActionContinue + } + config := state.Get("config").(*Config) client := state.Get("cvm_client").(*cvm.Client) diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index b1c3baf0..e2e40837 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -15,4 +15,6 @@ - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `skip_create_image` (bool) - Skip creating an image. When set to true, you don't need to enter target image information, share, copy, etc. The default value is false. + From 11c559e2c80fbbef7af51c848d5167ec2cd9c2e7 Mon Sep 17 00:00:00 2001 From: tongyiming Date: Tue, 20 May 2025 11:00:19 +0800 Subject: [PATCH 19/36] Delete .github/workflows/sync-code.yaml --- .github/workflows/sync-code.yaml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/sync-code.yaml diff --git a/.github/workflows/sync-code.yaml b/.github/workflows/sync-code.yaml deleted file mode 100644 index 63cbd4e5..00000000 --- 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 From d8a2a71d53e6a67b598e6a45dfb99b5f98634201 Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 21 May 2025 11:22:33 +0800 Subject: [PATCH 20/36] support concurrent --- builder/tencentcloud/cvm/run_config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index d7cc1711..2a359e33 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -116,7 +116,8 @@ var ValidCBSType = []string{ } func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { - packerId := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()[:8]) + uuid := uuid.TimeOrderedUUID() + packerId := fmt.Sprintf("packer_%s_%s", uuid[:8], uuid[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 From 43985923ec22069486334915feda83609ec6f3d0 Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 21 May 2025 11:33:04 +0800 Subject: [PATCH 21/36] update --- builder/tencentcloud/cvm/run_config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 2a359e33..82df220a 100644 --- a/builder/tencentcloud/cvm/run_config.go +++ b/builder/tencentcloud/cvm/run_config.go @@ -116,8 +116,8 @@ var ValidCBSType = []string{ } func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error { - uuid := uuid.TimeOrderedUUID() - packerId := fmt.Sprintf("packer_%s_%s", uuid[:8], uuid[9:13]) + 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 From 368296ebf085584b24dc220095b2c99e3d42c8b5 Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 4 Jun 2025 20:16:02 +0800 Subject: [PATCH 22/36] add datasource image --- .web-docs/components/builder/cvm/README.md | 8 +- builder/tencentcloud/cvm/access_config.go | 28 +-- builder/tencentcloud/cvm/builder.hcl2spec.go | 4 +- builder/tencentcloud/cvm/common.go | 24 +++ builder/tencentcloud/cvm/run_config.go | 8 + builder/tencentcloud/cvm/step_copy_image.go | 1 - .../tencentcloud/examples/image.pkr.hcl | 29 +++ datasource/tencentcloud/image/data.go | 178 ++++++++++++++++++ .../tencentcloud/image/data.hcl2spec.go | 95 ++++++++++ .../tencentcloud/image/data_acc_test.go | 66 +++++++ datasource/tencentcloud/image/data_test.go | 89 +++++++++ datasource/tencentcloud/image/sorting.go | 30 +++ .../image/test-fixtures/image.pkr.hcl | 30 +++ .../cvm/TencentCloudAccessConfig-required.mdx | 4 - .../cvm/TencentCloudRunConfig-required.mdx | 4 + go.mod | 3 + go.sum | 40 ++++ main.go | 2 + 18 files changed, 605 insertions(+), 38 deletions(-) create mode 100644 datasource/tencentcloud/examples/image.pkr.hcl create mode 100644 datasource/tencentcloud/image/data.go create mode 100644 datasource/tencentcloud/image/data.hcl2spec.go create mode 100644 datasource/tencentcloud/image/data_acc_test.go create mode 100644 datasource/tencentcloud/image/data_test.go create mode 100644 datasource/tencentcloud/image/sorting.go create mode 100644 datasource/tencentcloud/image/test-fixtures/image.pkr.hcl diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index d31707e6..71310d29 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. + diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go index 89545399..64402c97 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -7,7 +7,6 @@ package cvm import ( - "context" "fmt" "os" "strconv" @@ -76,10 +75,6 @@ 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"` @@ -131,17 +126,12 @@ func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) { err error cvm_client *cvm.Client vpc_client *vpc.Client - resp *cvm.DescribeZonesResponse ) if err = cf.validateRegion(); err != nil { return nil, nil, err } - if cf.Zone == "" { - return nil, nil, fmt.Errorf("parameter zone must be set") - } - if cvm_client, err = NewCvmClient(cf); err != nil { return nil, nil, err } @@ -150,23 +140,7 @@ func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) { return 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 - } - } - - return nil, nil, fmt.Errorf("unknown zone: %s", cf.Zone) + return cvm_client, vpc_client, nil } func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index c521b5ea..d1bcf1dc 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -22,7 +22,6 @@ 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"` SecurityToken *string `mapstructure:"security_token" required:"false" cty:"security_token" hcl:"security_token"` @@ -114,6 +113,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"` } @@ -140,7 +140,6 @@ 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}, "security_token": &hcldec.AttrSpec{Name: "security_token", Type: cty.String, Required: false}, @@ -232,6 +231,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/common.go b/builder/tencentcloud/cvm/common.go index 13ae9cb2..1369c02c 100644 --- a/builder/tencentcloud/cvm/common.go +++ b/builder/tencentcloud/cvm/common.go @@ -109,6 +109,30 @@ func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) ( return nil, nil } +// GetImages +func GetImages(ctx context.Context, client *cvm.Client, filters []*cvm.Filter) ([]*cvm.Image, error) { + req := cvm.NewDescribeImagesRequest() + if len(filters) > 0 { + req.Filters = filters + } + + var resp *cvm.DescribeImagesResponse + err := 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 resp.Response.ImageSet, nil + } + + return nil, nil +} + // NewCvmClient returns a new cvm client func NewCvmClient(cf *TencentCloudAccessConfig) (client *cvm.Client, err error) { apiV3Conn, err := packerConfigClient(cf) diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go index 82df220a..65ee5c3a 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{ @@ -125,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/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go index 096a1b0e..6e8f6b5a 100644 --- a/builder/tencentcloud/cvm/step_copy_image.go +++ b/builder/tencentcloud/cvm/step_copy_image.go @@ -58,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/datasource/tencentcloud/examples/image.pkr.hcl b/datasource/tencentcloud/examples/image.pkr.hcl new file mode 100644 index 00000000..2ff53488 --- /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 00000000..98c36eb3 --- /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 00000000..4fd94010 --- /dev/null +++ b/datasource/tencentcloud/image/data.hcl2spec.go @@ -0,0 +1,95 @@ +// 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"` + 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}, + "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 00000000..58c1b6d8 --- /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 00000000..e1a90d5f --- /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 00000000..7a6824c7 --- /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 00000000..09e876d3 --- /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-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-required.mdx index 3f93b9a8..80aff723 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/TencentCloudRunConfig-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudRunConfig-required.mdx index 7bc83eb7..dd2eaef3 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 b5dc20d6..65b66770 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,8 @@ require ( github.com/zclconf/go-cty v1.13.3 ) +require github.com/apparentlymart/go-textseg v1.0.0 // indirect + require ( cloud.google.com/go v0.105.0 // indirect cloud.google.com/go/compute v1.12.1 // indirect @@ -57,6 +59,7 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 github.com/hashicorp/serf v0.10.1 // indirect github.com/hashicorp/vault/api v1.10.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect diff --git a/go.sum b/go.sum index 2b448358..6ef2f827 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,7 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nu github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -32,6 +33,9 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/antchfx/xmlquery v1.3.5 h1:I7TuBRqsnfFuL11ruavGm911Awx9IqSdiU6W/ztSmVw= github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= @@ -50,6 +54,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -74,6 +79,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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= @@ -82,6 +88,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -131,6 +138,7 @@ github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 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= +github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -153,6 +161,7 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -185,6 +194,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 h1:PFfGModn55JA0oBsvFghhj0v93me+Ctr3uHC/UmFAls= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= @@ -197,8 +208,10 @@ github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -217,6 +230,7 @@ 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= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -249,6 +263,7 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -270,6 +285,9 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/ 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= github.com/nywilken/go-cty v1.13.3/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db h1:9uViuKtx1jrlXLBW/pMnhOfzn3iSEdLase/But/IZRU= github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -303,8 +321,10 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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= @@ -358,9 +378,12 @@ 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/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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= @@ -369,10 +392,12 @@ 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= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 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= @@ -384,12 +409,15 @@ golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -398,6 +426,7 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 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= @@ -410,15 +439,18 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -435,6 +467,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= @@ -448,6 +482,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= @@ -458,6 +493,8 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= @@ -503,6 +540,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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= @@ -516,3 +555,4 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= diff --git a/main.go b/main.go index 212f8fc5..3267a4e7 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 { From 86594a6c31160b5451ac9e8eba33dfdc40e14308 Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 4 Jun 2025 20:20:25 +0800 Subject: [PATCH 23/36] update --- builder/tencentcloud/cvm/common.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/builder/tencentcloud/cvm/common.go b/builder/tencentcloud/cvm/common.go index 1369c02c..13ae9cb2 100644 --- a/builder/tencentcloud/cvm/common.go +++ b/builder/tencentcloud/cvm/common.go @@ -109,30 +109,6 @@ func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) ( return nil, nil } -// GetImages -func GetImages(ctx context.Context, client *cvm.Client, filters []*cvm.Filter) ([]*cvm.Image, error) { - req := cvm.NewDescribeImagesRequest() - if len(filters) > 0 { - req.Filters = filters - } - - var resp *cvm.DescribeImagesResponse - err := 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 resp.Response.ImageSet, nil - } - - return nil, nil -} - // NewCvmClient returns a new cvm client func NewCvmClient(cf *TencentCloudAccessConfig) (client *cvm.Client, err error) { apiV3Conn, err := packerConfigClient(cf) From 446c9a2cc296b34256a1f26b69e17eb92d77f26d Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 4 Jun 2025 20:21:28 +0800 Subject: [PATCH 24/36] go mod tidy --- go.mod | 7 ++--- go.sum | 93 ---------------------------------------------------------- 2 files changed, 2 insertions(+), 98 deletions(-) diff --git a/go.mod b/go.mod index 65b66770..632c01fc 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,6 @@ require ( github.com/zclconf/go-cty v1.13.3 ) -require github.com/apparentlymart/go-textseg v1.0.0 // indirect - require ( cloud.google.com/go v0.105.0 // indirect cloud.google.com/go/compute v1.12.1 // indirect @@ -38,7 +36,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 @@ -59,7 +57,6 @@ require ( github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 github.com/hashicorp/serf v0.10.1 // indirect github.com/hashicorp/vault/api v1.10.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -72,7 +69,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 6ef2f827..77657f55 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= @@ -23,7 +18,6 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nu github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns= github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -33,9 +27,6 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/antchfx/xmlquery v1.3.5 h1:I7TuBRqsnfFuL11ruavGm911Awx9IqSdiU6W/ztSmVw= github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= -github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= @@ -54,7 +45,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -78,8 +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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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= @@ -88,7 +76,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -131,14 +118,11 @@ 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= -github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -153,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= @@ -161,7 +144,6 @@ github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= -github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -194,8 +176,6 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= -github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 h1:PFfGModn55JA0oBsvFghhj0v93me+Ctr3uHC/UmFAls= -github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= @@ -208,10 +188,8 @@ github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -230,7 +208,6 @@ 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= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -263,7 +240,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -275,7 +251,6 @@ 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= @@ -285,9 +260,6 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/ 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= github.com/nywilken/go-cty v1.13.3/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db h1:9uViuKtx1jrlXLBW/pMnhOfzn3iSEdLase/But/IZRU= github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -321,86 +293,43 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/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/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/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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= @@ -409,15 +338,12 @@ golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -426,31 +352,26 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 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= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -467,8 +388,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= @@ -482,7 +401,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= @@ -493,15 +411,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= 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= @@ -511,7 +426,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= @@ -519,7 +433,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= @@ -533,15 +446,11 @@ 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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= @@ -549,10 +458,8 @@ 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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= From 70dcdf2d0dbcf0d2ee5031baa573f0aab165b559 Mon Sep 17 00:00:00 2001 From: mikatong Date: Wed, 4 Jun 2025 20:25:34 +0800 Subject: [PATCH 25/36] update test --- builder/tencentcloud/cvm/run_config_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/tencentcloud/cvm/run_config_test.go b/builder/tencentcloud/cvm/run_config_test.go index 97536ea6..711f9748 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{ From 4ac0ea4ad95158788919151ae5354da43317fac7 Mon Sep 17 00:00:00 2001 From: mikatong Date: Thu, 5 Jun 2025 14:42:49 +0800 Subject: [PATCH 26/36] support snapshot tags --- .web-docs/components/builder/cvm/README.md | 5 +++ builder/tencentcloud/cvm/access_config.go | 24 ++++++---- builder/tencentcloud/cvm/builder.go | 3 +- builder/tencentcloud/cvm/builder.hcl2spec.go | 4 ++ builder/tencentcloud/cvm/client.go | 12 +++++ builder/tencentcloud/cvm/common.go | 45 +++++++++++++++++++ builder/tencentcloud/cvm/image_config.go | 4 ++ builder/tencentcloud/cvm/step_create_image.go | 11 +++++ datasource/tencentcloud/image/data.go | 4 +- .../tencentcloud/image/data.hcl2spec.go | 2 + .../TencentCloudAccessConfig-not-required.mdx | 3 ++ .../TencentCloudImageConfig-not-required.mdx | 2 + go.mod | 8 +++- go.sum | 11 +++-- 14 files changed, 123 insertions(+), 15 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 71310d29..9e4c9c45 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -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,8 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `snapshot_tags` (map[string]string) - Snapshot Tags + - `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 64402c97..2c353185 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -13,6 +13,7 @@ import ( "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" ) @@ -81,6 +82,9 @@ type TencentCloudAccessConfig struct { // 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 @@ -121,26 +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 + tag_client *tag.Client ) if err = cf.validateRegion(); err != nil { - return nil, nil, err + 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 + } + + if tag_client, err = NewTagClient(cf); err != nil { + return nil, nil, nil, err } - return cvm_client, vpc_client, nil + return cvm_client, vpc_client, tag_client, nil } func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { @@ -150,9 +159,8 @@ 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 == "" { + 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 c73d5d8c..6718d3ee 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,6 +84,7 @@ 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) diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index d1bcf1dc..976a1d62 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -24,6 +24,7 @@ type FlatConfig struct { 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 *FlatTencentCloudAccessRole `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"` Profile *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"` @@ -35,6 +36,7 @@ type FlatConfig struct { ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false" cty:"image_copy_regions" hcl:"image_copy_regions"` ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false" cty:"image_share_accounts" hcl:"image_share_accounts"` ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"` + 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"` @@ -142,6 +144,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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((*FlatTencentCloudAccessRole)(nil).HCL2Spec())}, "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, @@ -153,6 +156,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_copy_regions": &hcldec.AttrSpec{Name: "image_copy_regions", Type: cty.List(cty.String), Required: false}, "image_share_accounts": &hcldec.AttrSpec{Name: "image_share_accounts", Type: cty.List(cty.String), Required: false}, "image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.Map(cty.String), Required: false}, + "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}, diff --git a/builder/tencentcloud/cvm/client.go b/builder/tencentcloud/cvm/client.go index 7f6bef26..1d83edcc 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 13ae9cb2..ff514abf 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 9b207c3c..6d8b416b 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -32,6 +32,7 @@ type TencentCloudImageConfig struct { 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"` + 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"` @@ -79,6 +80,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/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index 1913ed33..b78f2bad 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -9,6 +9,7 @@ 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 { @@ -18,6 +19,7 @@ type stepCreateImage struct { 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) @@ -106,6 +108,15 @@ 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 { + resourceName := BuildTagResourceName("cvm", "snapshot", config.Region, *snapshot.SnapshotId) + err := AddResourceTag(ctx, tagClient, resourceName, snapshotTags) + return Halt(state, err, "Failed to set tag for snapshot") + } + } + tencentCloudImages := make(map[string]string) tencentCloudImages[config.Region] = s.imageId state.Put("tencentcloudimages", tencentCloudImages) diff --git a/datasource/tencentcloud/image/data.go b/datasource/tencentcloud/image/data.go index 98c36eb3..d096cc2e 100644 --- a/datasource/tencentcloud/image/data.go +++ b/datasource/tencentcloud/image/data.go @@ -105,7 +105,7 @@ func (d *Datasource) Execute() (cty.Value, error) { } func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { - client, _, err := d.config.Client() + client, _, _, err := d.config.Client() if err != nil { return nil, err } @@ -150,7 +150,7 @@ func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { } func (d *Datasource) ResolveImageByImageFamily() (*cvm.Image, error) { - client, _, err := d.config.Client() + client, _, _, err := d.config.Client() if err != nil { return nil, err } diff --git a/datasource/tencentcloud/image/data.hcl2spec.go b/datasource/tencentcloud/image/data.hcl2spec.go index 4fd94010..7327099a 100644 --- a/datasource/tencentcloud/image/data.hcl2spec.go +++ b/datasource/tencentcloud/image/data.hcl2spec.go @@ -24,6 +24,7 @@ type FlatConfig struct { 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"` @@ -58,6 +59,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx index 67405b86..7bb930b5 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/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index e2e40837..8791eff2 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -15,6 +15,8 @@ - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. +- `snapshot_tags` (map[string]string) - Snapshot Tags + - `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/go.mod b/go.mod index 632c01fc..6c5fad98 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 diff --git a/go.sum b/go.sum index 77657f55..104dac73 100644 --- a/go.sum +++ b/go.sum @@ -204,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= @@ -256,6 +255,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ 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= @@ -306,12 +307,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 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.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/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/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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -449,8 +453,9 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw 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= From 23f5b18ca4638a2ca0ee269e442681ffa3e34815 Mon Sep 17 00:00:00 2001 From: mikatong Date: Thu, 5 Jun 2025 15:11:12 +0800 Subject: [PATCH 27/36] update --- builder/tencentcloud/cvm/access_config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go index 2c353185..cecf8595 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -159,7 +159,8 @@ func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { errs = append(errs, err) } - if cf.CvmEndpoint == "" || cf.VpcEndpoint == "" || cf.TagEndpoint == "" { + 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")) } From 4c03d82167968a80432623a8b784f9875fa5ae55 Mon Sep 17 00:00:00 2001 From: mikatong Date: Thu, 5 Jun 2025 17:27:27 +0800 Subject: [PATCH 28/36] update doc --- .web-docs/components/builder/cvm/README.md | 2 +- builder/tencentcloud/cvm/image_config.go | 3 ++- builder/tencentcloud/cvm/step_create_image.go | 7 ++++++- .../cvm/TencentCloudImageConfig-not-required.mdx | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index 9e4c9c45..c7f60fdd 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -104,7 +104,7 @@ 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) - Snapshot Tags +- `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/image_config.go b/builder/tencentcloud/cvm/image_config.go index 6d8b416b..75a948d9 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -31,7 +31,8 @@ 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. diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index b78f2bad..3fd164aa 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -111,9 +111,14 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul 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) - return Halt(state, err, "Failed to set tag for snapshot") + if err != nil { + return Halt(state, err, fmt.Sprintf("Failed to set tag for snapshot(%s)", *snapshot.SnapshotId)) + } } } diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index 8791eff2..21a382cf 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -15,7 +15,7 @@ - `image_tags` (map[string]string) - Key/value pair tags that will be applied to the resulting image. -- `snapshot_tags` (map[string]string) - Snapshot Tags +- `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. From bd056e065d4e872358ed784c1f370f379a0d117c Mon Sep 17 00:00:00 2001 From: arunma Date: Mon, 21 Jul 2025 11:23:19 +0800 Subject: [PATCH 29/36] feat: Support organization account and other accounts sharing --- .web-docs/components/builder/cvm/README.md | 11 +++ builder/tencentcloud/cvm/access_config.go | 43 +++++--- builder/tencentcloud/cvm/builder.go | 18 +++- builder/tencentcloud/cvm/builder.hcl2spec.go | 6 ++ builder/tencentcloud/cvm/client.go | 25 +++++ builder/tencentcloud/cvm/common.go | 31 ++++++ builder/tencentcloud/cvm/image_config.go | 8 ++ builder/tencentcloud/cvm/step_create_image.go | 1 + builder/tencentcloud/cvm/step_share_image.go | 98 ++++++++++++++++++- .../tencentcloud/image/data.hcl2spec.go | 2 + .../TencentCloudAccessConfig-not-required.mdx | 3 + .../TencentCloudImageConfig-not-required.mdx | 8 ++ go.mod | 4 +- go.sum | 6 ++ 14 files changed, 239 insertions(+), 25 deletions(-) diff --git a/.web-docs/components/builder/cvm/README.md b/.web-docs/components/builder/cvm/README.md index c7f60fdd..e0a7ff14 100644 --- a/.web-docs/components/builder/cvm/README.md +++ b/.web-docs/components/builder/cvm/README.md @@ -62,6 +62,9 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `tag_endpoint` (string) - The endpoint you want to reach the cloud endpoint, if tce cloud you should set a tce tag endpoint. +- `org_endpoint` (string) - The endpoint you want to reach the cloud endpoint, + if tce cloud you should set a tce organization 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`. @@ -108,6 +111,14 @@ a [communicator](/packer/docs/templates/legacy_json_templates/communicator) can - `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. +- `is_share_org_members` (bool) - After creating the image, + whether to share it with other accounts in the organization + where the current account is located. + The image can be copied to a maximum of 50 accounts, + with ImageShareAccounts being the priority. + +- `image_family` (string) - Image family. Example value: business-daily-update. + diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go index cecf8595..f5992696 100644 --- a/builder/tencentcloud/cvm/access_config.go +++ b/builder/tencentcloud/cvm/access_config.go @@ -12,9 +12,6 @@ import ( "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" ) const ( @@ -85,6 +82,9 @@ type TencentCloudAccessConfig struct { // 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 endpoint you want to reach the cloud endpoint, + // if tce cloud you should set a tce organization endpoint. + OrgEndpoint string `mapstructure:"org_endpoint" required:"false"` // The region validation can be skipped if this value is true, the default // value is false. skipValidation bool @@ -125,31 +125,42 @@ type TencentCloudAccessRole struct { SessionDuration int `mapstructure:"session_duration" required:"false"` } -func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, *tag.Client, error) { +func (cf *TencentCloudAccessConfig) Client() (map[string]interface{}, error) { var ( - err error - cvm_client *cvm.Client - vpc_client *vpc.Client - tag_client *tag.Client + err error + // cvm_client *cvm.Client + // vpc_client *vpc.Client + // tag_client *tag.Client + // org_client *org.Client + // cam_client *cam.Client ) if err = cf.validateRegion(); err != nil { - return nil, nil, nil, err + return nil, err + } + + clientMap := map[string]interface{}{} + if clientMap["cvm_client"], err = NewCvmClient(cf); err != nil { + return nil, err + } + + if clientMap["vpc_client"], err = NewVpcClient(cf); err != nil { + return nil, err } - if cvm_client, err = NewCvmClient(cf); err != nil { - return nil, nil, nil, err + if clientMap["tag_client"], err = NewTagClient(cf); err != nil { + return nil, err } - if vpc_client, err = NewVpcClient(cf); err != nil { - return nil, nil, nil, err + if clientMap["org_client"], err = NewOrgClient(cf); err != nil { + return nil, err } - if tag_client, err = NewTagClient(cf); err != nil { - return nil, nil, nil, err + if clientMap["cam_client"], err = NewCamClient(cf); err != nil { + return nil, err } - return cvm_client, vpc_client, tag_client, nil + return clientMap, nil } func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 6718d3ee..d814e8fb 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -17,6 +17,11 @@ import ( packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/template/config" "github.com/hashicorp/packer-plugin-sdk/template/interpolate" + cam "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam/v20190116" + vm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + org "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization/v20210331" + tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" ) const BuilderId = "tencent.cloud" @@ -75,16 +80,18 @@ 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, tagClient, err := b.config.Client() + clientMap, err := b.config.Client() if err != nil { return nil, err } state := new(multistep.BasicStateBag) state.Put("config", &b.config) - state.Put("cvm_client", cvmClient) - state.Put("vpc_client", vpcClient) - state.Put("tag_client", tagClient) + state.Put("cvm_client", clientMap["cvm_client"].(*vm.Client)) + state.Put("vpc_client", clientMap["vpc_client"].(*vpc.Client)) + state.Put("tag_client", clientMap["tag_client"].(*tag.Client)) + state.Put("org_client", clientMap["org_client"].(*org.Client)) + state.Put("cam_client", clientMap["cam_client"].(*cam.Client)) state.Put("hook", hook) state.Put("ui", ui) @@ -155,6 +162,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) }, &stepShareImage{ b.config.ImageShareAccounts, + b.config.IsShareOrgMembers, }, &stepCopyImage{ DestinationRegions: b.config.ImageCopyRegions, @@ -177,7 +185,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) artifact := &Artifact{ TencentCloudImages: state.Get("tencentcloudimages").(map[string]string), BuilderIdValue: BuilderId, - Client: cvmClient, + Client: clientMap["cvm_client"].(*vm.Client), StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, } diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 976a1d62..ba1cd00c 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -25,6 +25,7 @@ type FlatConfig struct { 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"` + OrgEndpoint *string `mapstructure:"org_endpoint" required:"false" cty:"org_endpoint" hcl:"org_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"` @@ -38,6 +39,8 @@ type FlatConfig struct { 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"` + IsShareOrgMembers *bool `mapstructure:"is_share_org_members" required:"false" cty:"is_share_org_members" hcl:"is_share_org_members"` + ImageFamily *string `mapstructure:"image_family" required:"false" cty:"image_family" hcl:"image_family"` 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"` @@ -145,6 +148,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "org_endpoint": &hcldec.AttrSpec{Name: "org_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}, @@ -158,6 +162,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "is_share_org_members": &hcldec.AttrSpec{Name: "is_share_org_members", Type: cty.Bool, Required: false}, + "image_family": &hcldec.AttrSpec{Name: "image_family", Type: cty.String, Required: false}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, "source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/client.go b/builder/tencentcloud/cvm/client.go index 1d83edcc..f738cf29 100644 --- a/builder/tencentcloud/cvm/client.go +++ b/builder/tencentcloud/cvm/client.go @@ -4,9 +4,11 @@ package cvm import ( + cam "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam/v20190116" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + org "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization/v20210331" 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" @@ -22,6 +24,8 @@ type TencentCloudClient struct { cvmConn *cvm.Client stsConn *sts.Client tagConn *tag.Client + orgConn *org.Client + camConn *cam.Client } func (me *TencentCloudClient) UseVpcClient(cpf *profile.ClientProfile) *vpc.Client { @@ -65,3 +69,24 @@ func (me *TencentCloudClient) UseTagClient(cpf *profile.ClientProfile) *tag.Clie return me.tagConn } + +func (me *TencentCloudClient) UseOrgClient(cpf *profile.ClientProfile) *org.Client { + if me.orgConn != nil { + return me.orgConn + } + + me.orgConn, _ = org.NewClient(me.Credential, me.Region, cpf) + + return me.orgConn +} + +func (me *TencentCloudClient) UseCamClient() *cam.Client { + if me.camConn != nil { + return me.camConn + } + + cpf := me.ClientProfile + me.camConn, _ = cam.NewClient(me.Credential, me.Region, cpf) + + return me.camConn +} diff --git a/builder/tencentcloud/cvm/common.go b/builder/tencentcloud/cvm/common.go index ff514abf..adda19c1 100644 --- a/builder/tencentcloud/cvm/common.go +++ b/builder/tencentcloud/cvm/common.go @@ -14,10 +14,12 @@ import ( "github.com/hashicorp/packer-plugin-sdk/multistep" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/retry" + cam "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam/v20190116" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + org "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization/v20210331" 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" @@ -181,6 +183,35 @@ func NewTagClient(cf *TencentCloudAccessConfig) (client *tag.Client, err error) return } +// NewVpcClient returns a new organization client +func NewOrgClient(cf *TencentCloudAccessConfig) (client *org.Client, err error) { + apiV3Conn, err := packerConfigClient(cf) + if err != nil { + return nil, err + } + + orgClientProfile, err := newClientProfile(cf.OrgEndpoint) + if err != nil { + return nil, err + } + + client = apiV3Conn.UseOrgClient(orgClientProfile) + + return +} + +// NewCamClient returns a new cam client +func NewCamClient(cf *TencentCloudAccessConfig) (client *cam.Client, err error) { + apiV3Conn, err := packerConfigClient(cf) + if err != nil { + return nil, err + } + + client = apiV3Conn.UseCamClient() + + return +} + // CheckResourceIdFormat check resource id format func CheckResourceIdFormat(resource string, id string) bool { regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource)) diff --git a/builder/tencentcloud/cvm/image_config.go b/builder/tencentcloud/cvm/image_config.go index 75a948d9..63a9b148 100644 --- a/builder/tencentcloud/cvm/image_config.go +++ b/builder/tencentcloud/cvm/image_config.go @@ -37,6 +37,14 @@ type TencentCloudImageConfig struct { 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"` + // After creating the image, + // whether to share it with other accounts in the organization + // where the current account is located. + // The image can be copied to a maximum of 50 accounts, + // with ImageShareAccounts being the priority. + IsShareOrgMembers bool `mapstructure:"is_share_org_members" required:"false"` + // Image family. Example value: business-daily-update. + ImageFamily string `mapstructure:"image_family" required:"false"` } func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go index 3fd164aa..bb823404 100644 --- a/builder/tencentcloud/cvm/step_create_image.go +++ b/builder/tencentcloud/cvm/step_create_image.go @@ -35,6 +35,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul req := cvm.NewCreateImageRequest() req.ImageName = &config.ImageName req.ImageDescription = &config.ImageDescription + req.ImageFamily = &config.ImageFamily req.InstanceId = instance.InstanceId // TODO: We should allow user to specify which data disk should be diff --git a/builder/tencentcloud/cvm/step_share_image.go b/builder/tencentcloud/cvm/step_share_image.go index 821555bf..e4f8350e 100644 --- a/builder/tencentcloud/cvm/step_share_image.go +++ b/builder/tencentcloud/cvm/step_share_image.go @@ -6,19 +6,23 @@ package cvm import ( "context" "fmt" + "strconv" "strings" "github.com/hashicorp/packer-plugin-sdk/multistep" + cam "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam/v20190116" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + organization "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization/v20210331" ) type stepShareImage struct { - ShareAccounts []string + ShareAccounts []string + IsShareOrgMembers bool } func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - if len(s.ShareAccounts) == 0 { + if len(s.ShareAccounts) == 0 && !s.IsShareOrgMembers { return multistep.ActionContinue } @@ -30,10 +34,23 @@ func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) mult req := cvm.NewModifyImageSharePermissionRequest() req.ImageId = imageId req.Permission = common.StringPtr("SHARE") - accounts := make([]*string, 0, len(s.ShareAccounts)) + accounts := []*string{} for _, account := range s.ShareAccounts { accounts = append(accounts, common.StringPtr(account)) } + + if s.IsShareOrgMembers { + accountList, err := s.getOrgAccounts(ctx, state) + if err != nil { + return Halt(state, err, "Failed to get org accounts") + } + accounts = append(accounts, accountList...) + } + + if len(accounts) == 0 { + return multistep.ActionContinue + } + req.AccountIds = accounts err := Retry(ctx, func(ctx context.Context) error { _, e := client.ModifyImageSharePermission(req) @@ -48,6 +65,81 @@ func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) mult return multistep.ActionContinue } +func (s *stepShareImage) getOrgAccounts(ctx context.Context, state multistep.StateBag) ([]*string, error) { + + currentAccount, err := s.getUserId(ctx, state) + if err != nil { + return nil, err + } + + req := organization.NewDescribeOrganizationMembersRequest() + resp := organization.NewDescribeOrganizationMembersResponse() + + var limit uint64 = 50 + var offset uint64 = 0 + + req.Limit = &limit + req.Offset = &offset + + accounts := []*string{} + for { + client := state.Get("org_client").(*organization.Client) + err := Retry(ctx, func(ctx context.Context) error { + var e error + resp, e = client.DescribeOrganizationMembers(req) + return e + }) + if err != nil { + return nil, nil + } + if resp.Response == nil { + return nil, nil + } + items := resp.Response.Items + for _, v := range items { + if v.MemberUin != nil { + if strconv.FormatInt(*v.MemberUin, 10) == currentAccount { + continue + } + accounts = append(accounts, common.StringPtr(strconv.Itoa(int(*v.MemberUin)))) + } + } + + if len(items) < int(limit) { + break + } + + offset += limit + } + + return accounts, nil +} + +func (s *stepShareImage) getUserId(ctx context.Context, state multistep.StateBag) (string, error) { + req := cam.NewGetUserAppIdRequest() + resp := cam.NewGetUserAppIdResponse() + + client := state.Get("cam_client").(*cam.Client) + err := Retry(ctx, func(ctx context.Context) error { + var e error + resp, e = client.GetUserAppId(req) + return e + }) + if err != nil { + return "", err + } + + if resp.Response == nil { + return "", nil + } + + if resp.Response.Uin != nil { + return *resp.Response.Uin, nil + } + + return "", nil +} + func (s *stepShareImage) Cleanup(state multistep.StateBag) { _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) diff --git a/datasource/tencentcloud/image/data.hcl2spec.go b/datasource/tencentcloud/image/data.hcl2spec.go index 7327099a..f870f0de 100644 --- a/datasource/tencentcloud/image/data.hcl2spec.go +++ b/datasource/tencentcloud/image/data.hcl2spec.go @@ -25,6 +25,7 @@ type FlatConfig struct { 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"` + OrgEndpoint *string `mapstructure:"org_endpoint" required:"false" cty:"org_endpoint" hcl:"org_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"` @@ -60,6 +61,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, + "org_endpoint": &hcldec.AttrSpec{Name: "org_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}, diff --git a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx index 7bb930b5..8c15fe1d 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudAccessConfig-not-required.mdx @@ -9,6 +9,9 @@ - `tag_endpoint` (string) - The endpoint you want to reach the cloud endpoint, if tce cloud you should set a tce tag endpoint. +- `org_endpoint` (string) - The endpoint you want to reach the cloud endpoint, + if tce cloud you should set a tce organization 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/TencentCloudImageConfig-not-required.mdx b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx index 21a382cf..8859424a 100644 --- a/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx +++ b/docs-partials/builder/tencentcloud/cvm/TencentCloudImageConfig-not-required.mdx @@ -19,4 +19,12 @@ - `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. +- `is_share_org_members` (bool) - After creating the image, + whether to share it with other accounts in the organization + where the current account is located. + The image can be copied to a maximum of 50 accounts, + with ImageShareAccounts being the priority. + +- `image_family` (string) - Image family. Example value: business-daily-update. + diff --git a/go.mod b/go.mod index 6c5fad98..ca56c5d5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ 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.1175 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1200 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 @@ -16,6 +16,8 @@ require ( require ( github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam v1.0.1200 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/organization v1.0.1200 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) diff --git a/go.sum b/go.sum index 104dac73..f9e0ff20 100644 --- a/go.sum +++ b/go.sum @@ -306,12 +306,18 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/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.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam v1.0.1200 h1:vONBPPPZR83iIJGnrIBjIrFj1Lba8p3tBmwmveabLlo= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cam v1.0.1200/go.mod h1:g1HW2Y5P+yi9XH2dL136gEMKogKcNRLoZ4hq9767D48= 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.1072/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= 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/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/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/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= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sts v1.0.797/go.mod h1:IugQh1ZI86ZeEUBYf+u/REwTeKZcneP449FPU8BbLxA= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag v1.0.1175 h1:Yw7azOzlQqh6/Zyk5JCLH69LJ7nE2jkilGXU+3vIqUM= From c916d99e108cd1fcc0a53a8f6d5267c88dcede99 Mon Sep 17 00:00:00 2001 From: arunma Date: Mon, 21 Jul 2025 11:26:53 +0800 Subject: [PATCH 30/36] fix: modify get client --- datasource/tencentcloud/image/data.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/datasource/tencentcloud/image/data.go b/datasource/tencentcloud/image/data.go index d096cc2e..bdec647c 100644 --- a/datasource/tencentcloud/image/data.go +++ b/datasource/tencentcloud/image/data.go @@ -105,11 +105,13 @@ func (d *Datasource) Execute() (cty.Value, error) { } func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { - client, _, _, err := d.config.Client() + clientMap, err := d.config.Client() if err != nil { return nil, err } + cvmClient := clientMap["cvm_client"].(*cvm.Client) + req := cvm.NewDescribeImagesRequest() var filters []*cvm.Filter @@ -127,7 +129,7 @@ func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { var resp *cvm.DescribeImagesResponse err = buildCvm.Retry(ctx, func(ctx context.Context) error { var e error - resp, e = client.DescribeImages(req) + resp, e = cvmClient.DescribeImages(req) return e }) if err != nil { @@ -150,11 +152,13 @@ func (d *Datasource) ResolveImageByFilters() (*cvm.Image, error) { } func (d *Datasource) ResolveImageByImageFamily() (*cvm.Image, error) { - client, _, _, err := d.config.Client() + clientMap, err := d.config.Client() if err != nil { return nil, err } + cvmClient := clientMap["cvm_client"].(*cvm.Client) + var resp *cvm.DescribeImageFromFamilyResponse req := cvm.NewDescribeImageFromFamilyRequest() req.ImageFamily = &d.config.ImageFamily @@ -162,7 +166,7 @@ func (d *Datasource) ResolveImageByImageFamily() (*cvm.Image, error) { ctx := context.TODO() err = buildCvm.Retry(ctx, func(ctx context.Context) error { var e error - resp, e = client.DescribeImageFromFamily(req) + resp, e = cvmClient.DescribeImageFromFamily(req) return e }) From 5332df30d408fb635e9737ee520f1a38214dea9e Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 26 Sep 2025 17:13:04 +0800 Subject: [PATCH 31/36] 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 32/36] 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 33/36] 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 34/36] 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` From de8f66bbb65f34aa70700dc49e42243b8e0f3c54 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 24 Oct 2025 10:53:39 +0800 Subject: [PATCH 35/36] fix: Fixed the issue of using the wrong image when creating an instance based on an image family --- builder/tencentcloud/cvm/step_check_source_image.go | 3 +++ builder/tencentcloud/cvm/step_run_instance.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go index 616c4734..aed8e259 100644 --- a/builder/tencentcloud/cvm/step_check_source_image.go +++ b/builder/tencentcloud/cvm/step_check_source_image.go @@ -23,6 +23,7 @@ func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag ) config := state.Get("config").(*Config) client := state.Get("cvm_client").(*cvm.Client) + source_image := state.Get("source_image").(*cvm.Image) Say(state, config.SourceImageId, "Trying to check source image") @@ -30,6 +31,8 @@ func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag req.InstanceType = &config.InstanceType if config.SourceImageId != "" { req.ImageIds = []*string{&config.SourceImageId} + } else if *source_image.ImageId != "" { + req.ImageIds = []*string{source_image.ImageId} } else { imageNameRegex, err = regexp.Compile(config.SourceImageName) if err != nil { diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go index 8220640e..6ec2449d 100644 --- a/builder/tencentcloud/cvm/step_run_instance.go +++ b/builder/tencentcloud/cvm/step_run_instance.go @@ -55,7 +55,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul return Halt(state, err, "Failed to get user_data") } - Say(state, "Trying to create a new instance", "") + Say(state, *source_image.ImageId, "Try to create a new instance based on image") // config RunInstances parameters req := cvm.NewRunInstancesRequest() From d0d9f553539485a4ea2c1ac7919e6149d50c12b8 Mon Sep 17 00:00:00 2001 From: arunma Date: Fri, 24 Oct 2025 14:19:14 +0800 Subject: [PATCH 36/36] fix: Exception hand --- builder/tencentcloud/cvm/client.go | 7 +- .../cvm/step_check_source_image.go | 8 +- builder/tencentcloud/cvm/transport.go | 104 ++++++++++++++++++ 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 builder/tencentcloud/cvm/transport.go diff --git a/builder/tencentcloud/cvm/client.go b/builder/tencentcloud/cvm/client.go index f738cf29..5eb73c6b 100644 --- a/builder/tencentcloud/cvm/client.go +++ b/builder/tencentcloud/cvm/client.go @@ -34,7 +34,7 @@ func (me *TencentCloudClient) UseVpcClient(cpf *profile.ClientProfile) *vpc.Clie } me.vpcConn, _ = vpc.NewClient(me.Credential, me.Region, cpf) - // me.vpcConn.WithHttpTransport(&LogRoundTripper{}) + me.vpcConn.WithHttpTransport(&LogRoundTripper{}) return me.vpcConn } @@ -45,6 +45,7 @@ func (me *TencentCloudClient) UseCvmClient(cpf *profile.ClientProfile) *cvm.Clie } me.cvmConn, _ = cvm.NewClient(me.Credential, me.Region, cpf) + me.cvmConn.WithHttpTransport(&LogRoundTripper{}) return me.cvmConn } @@ -56,6 +57,7 @@ func (me *TencentCloudClient) UseStsClient() *sts.Client { cpf := me.ClientProfile me.stsConn, _ = sts.NewClient(me.Credential, me.Region, cpf) + me.stsConn.WithHttpTransport(&LogRoundTripper{}) return me.stsConn } @@ -66,6 +68,7 @@ func (me *TencentCloudClient) UseTagClient(cpf *profile.ClientProfile) *tag.Clie } me.tagConn, _ = tag.NewClient(me.Credential, me.Region, cpf) + me.tagConn.WithHttpTransport(&LogRoundTripper{}) return me.tagConn } @@ -76,6 +79,7 @@ func (me *TencentCloudClient) UseOrgClient(cpf *profile.ClientProfile) *org.Clie } me.orgConn, _ = org.NewClient(me.Credential, me.Region, cpf) + me.orgConn.WithHttpTransport(&LogRoundTripper{}) return me.orgConn } @@ -87,6 +91,7 @@ func (me *TencentCloudClient) UseCamClient() *cam.Client { cpf := me.ClientProfile me.camConn, _ = cam.NewClient(me.Credential, me.Region, cpf) + me.camConn.WithHttpTransport(&LogRoundTripper{}) return me.camConn } diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go index aed8e259..acbad68e 100644 --- a/builder/tencentcloud/cvm/step_check_source_image.go +++ b/builder/tencentcloud/cvm/step_check_source_image.go @@ -23,7 +23,11 @@ func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag ) config := state.Get("config").(*Config) client := state.Get("cvm_client").(*cvm.Client) - source_image := state.Get("source_image").(*cvm.Image) + + var source_image *cvm.Image + if state.Get("source_image") != nil { + source_image = state.Get("source_image").(*cvm.Image) + } Say(state, config.SourceImageId, "Trying to check source image") @@ -31,7 +35,7 @@ func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag req.InstanceType = &config.InstanceType if config.SourceImageId != "" { req.ImageIds = []*string{&config.SourceImageId} - } else if *source_image.ImageId != "" { + } else if source_image != nil && *source_image.ImageId != "" { req.ImageIds = []*string{source_image.ImageId} } else { imageNameRegex, err = regexp.Compile(config.SourceImageName) diff --git a/builder/tencentcloud/cvm/transport.go b/builder/tencentcloud/cvm/transport.go new file mode 100644 index 00000000..b98ce37c --- /dev/null +++ b/builder/tencentcloud/cvm/transport.go @@ -0,0 +1,104 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cvm + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "time" + + "github.com/hashicorp/packer-plugin-tencentcloud/version" +) + +type LogRoundTripper struct{} + +func (me *LogRoundTripper) RoundTrip(request *http.Request) (response *http.Response, errRet error) { + + var inBytes, outBytes []byte + + var start = time.Now() + + defer func() { me.log(inBytes, outBytes, errRet, start) }() + + bodyReader, errRet := request.GetBody() + if errRet != nil { + return + } + + var headName = "X-TC-Action" + var reqClientFormat = version.PluginVersion + request.Header.Set("X-TC-RequestClient", reqClientFormat.String()) + inBytes = []byte(fmt.Sprintf("%s, request: ", request.Header[headName])) + requestBody, errRet := ioutil.ReadAll(bodyReader) + if errRet != nil { + return + } + + inBytes = append(inBytes, requestBody...) + headName = "X-TC-Region" + appendMessage := []byte(fmt.Sprintf( + ", (host %+v, region:%+v)", + request.Header["Host"], + request.Header[headName], + )) + + inBytes = append(inBytes, appendMessage...) + response, errRet = http.DefaultTransport.RoundTrip(request) + if errRet != nil { + return + } + + outBytes, errRet = ioutil.ReadAll(response.Body) + if errRet != nil { + return + } + + response.Body = ioutil.NopCloser(bytes.NewBuffer(outBytes)) + return +} + +func (me *LogRoundTripper) log(in []byte, out []byte, err error, start time.Time) { + var buf bytes.Buffer + buf.WriteString("######") + tag := "[DEBUG]" + if err != nil { + tag = "[CRITICAL]" + } + + buf.WriteString(tag) + if len(in) > 0 { + buf.WriteString("tencentcloud-sdk-go: ") + buf.Write(in) + } + + if len(out) > 0 { + buf.WriteString("; response:") + err := json.Compact(&buf, out) + if err != nil { + out := bytes.Replace(out, + []byte("\n"), + []byte(""), + -1) + out = bytes.Replace(out, + []byte(" "), + []byte(""), + -1) + buf.Write(out) + } + } + + if err != nil { + buf.WriteString("; error:") + buf.WriteString(err.Error()) + } + + costFormat := fmt.Sprintf(",cost %s", time.Since(start).String()) + buf.WriteString(costFormat) + + log.Println(buf.String()) +}