Skip to content

Feat/support image family #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .web-docs/components/builder/cvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down Expand Up @@ -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.

<!-- End of code generated from the comments of the TencentCloudImageConfig struct in builder/tencentcloud/cvm/image_config.go; -->


Expand Down
43 changes: 27 additions & 16 deletions builder/tencentcloud/cvm/access_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
18 changes: 13 additions & 5 deletions builder/tencentcloud/cvm/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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,
Expand All @@ -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")},
}

Expand Down
6 changes: 6 additions & 0 deletions builder/tencentcloud/cvm/builder.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions builder/tencentcloud/cvm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
31 changes: 31 additions & 0 deletions builder/tencentcloud/cvm/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand Down
8 changes: 8 additions & 0 deletions builder/tencentcloud/cvm/image_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions builder/tencentcloud/cvm/step_create_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading