Extract Tencent Cloud (#10967)

* extract and vendor tencentcloud plugin

* fix fmt
This commit is contained in:
Sylvia Moss 2021-04-22 15:21:34 +02:00 committed by GitHub
parent ef612c0eb1
commit bcb25f1916
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 20 additions and 3432 deletions

View File

@ -1,159 +0,0 @@
//go:generate packer-sdc struct-markdown
package cvm
import (
"context"
"fmt"
"os"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
type Region string
// below would be moved to tencentcloud sdk git repo
const (
Bangkok = Region("ap-bangkok")
Beijing = Region("ap-beijing")
Chengdu = Region("ap-chengdu")
Chongqing = Region("ap-chongqing")
Guangzhou = Region("ap-guangzhou")
GuangzhouOpen = Region("ap-guangzhou-open")
Hongkong = Region("ap-hongkong")
Mumbai = Region("ap-mumbai")
Seoul = Region("ap-seoul")
Shanghai = Region("ap-shanghai")
ShanghaiFsi = Region("ap-shanghai-fsi")
ShenzhenFsi = Region("ap-shenzhen-fsi")
Singapore = Region("ap-singapore")
Tokyo = Region("ap-tokyo")
Frankfurt = Region("eu-frankfurt")
Moscow = Region("eu-moscow")
Ashburn = Region("na-ashburn")
Siliconvalley = Region("na-siliconvalley")
Toronto = Region("na-toronto")
)
var ValidRegions = []Region{
Bangkok, Beijing, Chengdu, Chongqing, Guangzhou, GuangzhouOpen, Hongkong, Shanghai,
ShanghaiFsi, ShenzhenFsi,
Mumbai, Seoul, Singapore, Tokyo, Moscow,
Frankfurt, Ashburn, Siliconvalley, Toronto,
}
type TencentCloudAccessConfig struct {
// Tencentcloud secret id. You should set it directly,
// or set the TENCENTCLOUD_ACCESS_KEY environment variable.
SecretId string `mapstructure:"secret_id" required:"true"`
// Tencentcloud secret key. You should set it directly,
// or set the TENCENTCLOUD_SECRET_KEY environment variable.
SecretKey string `mapstructure:"secret_key" required:"true"`
// The region where your cvm will be launch. You should
// reference Region and Zone
// for parameter taking.
Region string `mapstructure:"region" required:"true"`
// The zone where your cvm will be launch. You should
// reference Region and Zone
// for parameter taking.
Zone string `mapstructure:"zone" required:"true"`
// Do not check region and zone when validate.
SkipValidation bool `mapstructure:"skip_region_validation" required:"false"`
}
func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) {
var (
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.SecretId, cf.SecretKey, cf.Region); err != nil {
return nil, nil, err
}
if vpc_client, err = NewVpcClient(cf.SecretId, cf.SecretKey, cf.Region); err != nil {
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)
}
func (cf *TencentCloudAccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if err := cf.Config(); err != nil {
errs = append(errs, err)
}
if cf.Region == "" {
errs = append(errs, fmt.Errorf("parameter region must be set"))
} else if !cf.SkipValidation {
if err := cf.validateRegion(); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
func (cf *TencentCloudAccessConfig) Config() error {
if cf.SecretId == "" {
cf.SecretId = os.Getenv("TENCENTCLOUD_SECRET_ID")
}
if cf.SecretKey == "" {
cf.SecretKey = os.Getenv("TENCENTCLOUD_SECRET_KEY")
}
if cf.SecretId == "" || cf.SecretKey == "" {
return fmt.Errorf("parameter secret_id and secret_key must be set")
}
return nil
}
func (cf *TencentCloudAccessConfig) validateRegion() error {
return validRegion(cf.Region)
}
func validRegion(region string) error {
for _, valid := range ValidRegions {
if Region(region) == valid {
return nil
}
}
return fmt.Errorf("unknown region: %s", region)
}

View File

@ -1,31 +0,0 @@
package cvm
import (
"testing"
)
func TestTencentCloudAccessConfig_Prepare(t *testing.T) {
cf := TencentCloudAccessConfig{
SecretId: "secret-id",
SecretKey: "secret-key",
}
if err := cf.Prepare(nil); err == nil {
t.Fatal("should raise error: region not set")
}
cf.Region = "ap-guangzhou"
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't raise error: %v", err)
}
cf.Region = "unknown-region"
if err := cf.Prepare(nil); err == nil {
t.Fatal("should raise error: unknown region")
}
cf.SkipValidation = true
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't raise error: %v", err)
}
}

View File

@ -1,150 +0,0 @@
package cvm
import (
"context"
"fmt"
"log"
"sort"
"strings"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type Artifact struct {
TencentCloudImages map[string]string
BuilderIdValue string
Client *cvm.Client
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
func (a *Artifact) BuilderId() string {
return a.BuilderIdValue
}
func (*Artifact) Files() []string {
return nil
}
func (a *Artifact) Id() string {
parts := make([]string, 0, len(a.TencentCloudImages))
for region, imageId := range a.TencentCloudImages {
parts = append(parts, fmt.Sprintf("%s:%s", region, imageId))
}
sort.Strings(parts)
return strings.Join(parts, ",")
}
func (a *Artifact) String() string {
parts := make([]string, 0, len(a.TencentCloudImages))
for region, imageId := range a.TencentCloudImages {
parts = append(parts, fmt.Sprintf("%s: %s", region, imageId))
}
sort.Strings(parts)
return fmt.Sprintf("Tencentcloud images(%s) were created.\n\n", strings.Join(parts, "\n"))
}
func (a *Artifact) State(name string) interface{} {
if _, ok := a.StateData[name]; ok {
return a.StateData[name]
}
switch name {
case "atlas.artifact.metadata":
return a.stateAtlasMetadata()
default:
return nil
}
}
func (a *Artifact) Destroy() error {
ctx := context.TODO()
errors := make([]error, 0)
for region, imageId := range a.TencentCloudImages {
log.Printf("Delete tencentcloud image ID(%s) from region(%s)", imageId, region)
describeReq := cvm.NewDescribeImagesRequest()
describeReq.ImageIds = []*string{&imageId}
var describeResp *cvm.DescribeImagesResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
describeResp, e = a.Client.DescribeImages(describeReq)
return e
})
if err != nil {
errors = append(errors, err)
continue
}
if *describeResp.Response.TotalCount == 0 {
errors = append(errors, fmt.Errorf(
"describe images failed, region(%s) ImageId(%s)", region, imageId))
}
var shareAccountIds []*string = nil
describeShareReq := cvm.NewDescribeImageSharePermissionRequest()
describeShareReq.ImageId = &imageId
var describeShareResp *cvm.DescribeImageSharePermissionResponse
err = Retry(ctx, func(ctx context.Context) error {
var e error
describeShareResp, e = a.Client.DescribeImageSharePermission(describeShareReq)
return e
})
if err != nil {
errors = append(errors, err)
} else {
for _, sharePermission := range describeShareResp.Response.SharePermissionSet {
shareAccountIds = append(shareAccountIds, sharePermission.AccountId)
}
}
if len(shareAccountIds) != 0 {
cancelShareReq := cvm.NewModifyImageSharePermissionRequest()
cancelShareReq.ImageId = &imageId
cancelShareReq.AccountIds = shareAccountIds
CANCEL := "CANCEL"
cancelShareReq.Permission = &CANCEL
err := Retry(ctx, func(ctx context.Context) error {
_, e := a.Client.ModifyImageSharePermission(cancelShareReq)
return e
})
if err != nil {
errors = append(errors, err)
}
}
deleteReq := cvm.NewDeleteImagesRequest()
deleteReq.ImageIds = []*string{&imageId}
err = Retry(ctx, func(ctx context.Context) error {
_, e := a.Client.DeleteImages(deleteReq)
return e
})
if err != nil {
errors = append(errors, err)
}
}
if len(errors) == 1 {
return errors[0]
} else if len(errors) > 1 {
return &packersdk.MultiError{Errors: errors}
} else {
return nil
}
}
func (a *Artifact) stateAtlasMetadata() interface{} {
metadata := make(map[string]string)
for region, imageId := range a.TencentCloudImages {
k := fmt.Sprintf("region.%s", region)
metadata[k] = imageId
}
return metadata
}

View File

@ -1,163 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
const BuilderId = "tencent.cloud"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
TencentCloudAccessConfig `mapstructure:",squash"`
TencentCloudImageConfig `mapstructure:",squash"`
TencentCloudRunConfig `mapstructure:",squash"`
ctx interpolate.Context
}
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
PluginType: BuilderId,
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
b.config.ctx.EnableEnv = true
if err != nil {
return nil, nil, err
}
// Accumulate any errors
var errs *packersdk.MultiError
errs = packersdk.MultiErrorAppend(errs, b.config.TencentCloudAccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.TencentCloudImageConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.TencentCloudRunConfig.Prepare(&b.config.ctx)...)
if errs != nil && len(errs.Errors) > 0 {
return nil, nil, errs
}
packersdk.LogSecretFilter.Set(b.config.SecretId, b.config.SecretKey)
return nil, nil, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
cvmClient, vpcClient, 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("hook", hook)
state.Put("ui", ui)
// Build the steps
var steps []multistep.Step
steps = []multistep.Step{
&stepPreValidate{},
&stepCheckSourceImage{
b.config.SourceImageId,
},
&stepConfigKeyPair{
Debug: b.config.PackerDebug,
Comm: &b.config.Comm,
DebugKeyPath: fmt.Sprintf("cvm_%s.pem", b.config.PackerBuildName),
},
&stepConfigVPC{
VpcId: b.config.VpcId,
CidrBlock: b.config.CidrBlock,
VpcName: b.config.VpcName,
},
&stepConfigSubnet{
SubnetId: b.config.SubnetId,
SubnetCidrBlock: b.config.SubnectCidrBlock,
SubnetName: b.config.SubnetName,
Zone: b.config.Zone,
},
&stepConfigSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
SecurityGroupName: b.config.SecurityGroupName,
Description: "securitygroup for packer",
},
&stepRunInstance{
InstanceType: b.config.InstanceType,
UserData: b.config.UserData,
UserDataFile: b.config.UserDataFile,
ZoneId: b.config.Zone,
InstanceName: b.config.InstanceName,
DiskType: b.config.DiskType,
DiskSize: b.config.DiskSize,
DataDisks: b.config.DataDisks,
HostName: b.config.HostName,
InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
Tags: b.config.RunTags,
},
&communicator.StepConnect{
Config: &b.config.TencentCloudRunConfig.Comm,
SSHConfig: b.config.TencentCloudRunConfig.Comm.SSHConfigFunc(),
Host: SSHHost(b.config.AssociatePublicIpAddress),
},
&commonsteps.StepProvision{},
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.TencentCloudRunConfig.Comm,
},
// We need this step to detach keypair from instance, otherwise
// it always fails to delete the key.
&stepDetachTempKeyPair{},
&stepCreateImage{},
&stepShareImage{
b.config.ImageShareAccounts,
},
&stepCopyImage{
DesinationRegions: b.config.ImageCopyRegions,
SourceRegion: b.config.Region,
},
}
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
if _, ok := state.GetOk("image"); !ok {
return nil, nil
}
artifact := &Artifact{
TencentCloudImages: state.Get("tencentcloudimages").(map[string]string),
BuilderIdValue: BuilderId,
Client: cvmClient,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
return artifact, nil
}

View File

@ -1,220 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package cvm
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"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"`
Zone *string `mapstructure:"zone" required:"true" cty:"zone" hcl:"zone"`
SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation" hcl:"skip_region_validation"`
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"`
Reboot *bool `mapstructure:"reboot" required:"false" cty:"reboot" hcl:"reboot"`
ForcePoweroff *bool `mapstructure:"force_poweroff" required:"false" cty:"force_poweroff" hcl:"force_poweroff"`
Sysprep *bool `mapstructure:"sysprep" required:"false" cty:"sysprep" hcl:"sysprep"`
ImageForceDelete *bool `mapstructure:"image_force_delete" cty:"image_force_delete" hcl:"image_force_delete"`
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"`
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"`
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"`
DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"`
DiskSize *int64 `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
DataDisks []FlattencentCloudDataDisk `mapstructure:"data_disks" cty:"data_disks" hcl:"data_disks"`
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id" hcl:"vpc_id"`
VpcName *string `mapstructure:"vpc_name" required:"false" cty:"vpc_name" hcl:"vpc_name"`
VpcIp *string `mapstructure:"vpc_ip" cty:"vpc_ip" hcl:"vpc_ip"`
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
SubnetName *string `mapstructure:"subnet_name" required:"false" cty:"subnet_name" hcl:"subnet_name"`
CidrBlock *string `mapstructure:"cidr_block" required:"false" cty:"cidr_block" hcl:"cidr_block"`
SubnectCidrBlock *string `mapstructure:"subnect_cidr_block" required:"false" cty:"subnect_cidr_block" hcl:"subnect_cidr_block"`
InternetChargeType *string `mapstructure:"internet_charge_type" cty:"internet_charge_type" hcl:"internet_charge_type"`
InternetMaxBandwidthOut *int64 `mapstructure:"internet_max_bandwidth_out" required:"false" cty:"internet_max_bandwidth_out" hcl:"internet_max_bandwidth_out"`
SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id" hcl:"security_group_id"`
SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name" hcl:"security_group_name"`
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file" hcl:"user_data_file"`
HostName *string `mapstructure:"host_name" required:"false" cty:"host_name" hcl:"host_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"`
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"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
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"`
}
// 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},
"zone": &hcldec.AttrSpec{Name: "zone", Type: cty.String, Required: false},
"skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false},
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
"reboot": &hcldec.AttrSpec{Name: "reboot", Type: cty.Bool, Required: false},
"force_poweroff": &hcldec.AttrSpec{Name: "force_poweroff", Type: cty.Bool, Required: false},
"sysprep": &hcldec.AttrSpec{Name: "sysprep", Type: cty.Bool, Required: false},
"image_force_delete": &hcldec.AttrSpec{Name: "image_force_delete", Type: cty.Bool, Required: false},
"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},
"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},
"instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false},
"instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
"disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"data_disks": &hcldec.BlockListSpec{TypeName: "data_disks", Nested: hcldec.ObjectSpec((*FlattencentCloudDataDisk)(nil).HCL2Spec())},
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
"vpc_name": &hcldec.AttrSpec{Name: "vpc_name", Type: cty.String, Required: false},
"vpc_ip": &hcldec.AttrSpec{Name: "vpc_ip", Type: cty.String, Required: false},
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
"subnet_name": &hcldec.AttrSpec{Name: "subnet_name", Type: cty.String, Required: false},
"cidr_block": &hcldec.AttrSpec{Name: "cidr_block", Type: cty.String, Required: false},
"subnect_cidr_block": &hcldec.AttrSpec{Name: "subnect_cidr_block", Type: cty.String, Required: false},
"internet_charge_type": &hcldec.AttrSpec{Name: "internet_charge_type", Type: cty.String, Required: false},
"internet_max_bandwidth_out": &hcldec.AttrSpec{Name: "internet_max_bandwidth_out", Type: cty.Number, Required: false},
"security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false},
"security_group_name": &hcldec.AttrSpec{Name: "security_group_name", Type: cty.String, Required: false},
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
"host_name": &hcldec.AttrSpec{Name: "host_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())},
"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},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"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},
}
return s
}

View File

@ -1,229 +0,0 @@
package cvm
import (
"context"
"fmt"
"regexp"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/retry"
"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"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
// DefaultWaitForInterval is sleep interval when wait statue
const DefaultWaitForInterval = 5
// WaitForInstance wait for instance reaches statue
func WaitForInstance(ctx context.Context, client *cvm.Client, instanceId string, status string, timeout int) error {
req := cvm.NewDescribeInstancesRequest()
req.InstanceIds = []*string{&instanceId}
for {
var resp *cvm.DescribeInstancesResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = client.DescribeInstances(req)
return e
})
if err != nil {
return err
}
if *resp.Response.TotalCount == 0 {
return fmt.Errorf("instance(%s) not exist", instanceId)
}
if *resp.Response.InstanceSet[0].InstanceState == status &&
(resp.Response.InstanceSet[0].LatestOperationState == nil || *resp.Response.InstanceSet[0].LatestOperationState != "OPERATING") {
break
}
time.Sleep(DefaultWaitForInterval * time.Second)
timeout = timeout - DefaultWaitForInterval
if timeout <= 0 {
return fmt.Errorf("wait instance(%s) status(%s) timeout", instanceId, status)
}
}
return nil
}
// WaitForImageReady wait for image reaches statue
func WaitForImageReady(ctx context.Context, client *cvm.Client, imageName string, status string, timeout int) error {
for {
image, err := GetImageByName(ctx, client, imageName)
if err != nil {
return err
}
if image != nil && *image.ImageState == status {
return nil
}
time.Sleep(DefaultWaitForInterval * time.Second)
timeout = timeout - DefaultWaitForInterval
if timeout <= 0 {
return fmt.Errorf("wait image(%s) status(%s) timeout", imageName, status)
}
}
}
// GetImageByName get image by image name
func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) (*cvm.Image, error) {
req := cvm.NewDescribeImagesRequest()
req.Filters = []*cvm.Filter{
{
Name: common.StringPtr("image-name"),
Values: []*string{&imageName},
},
}
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 {
for _, image := range resp.Response.ImageSet {
if *image.ImageName == imageName {
return image, nil
}
}
}
return nil, nil
}
// NewCvmClient returns a new cvm client
func NewCvmClient(secretId, secretKey, region string) (client *cvm.Client, err error) {
cpf := profile.NewClientProfile()
cpf.HttpProfile.ReqMethod = "POST"
cpf.HttpProfile.ReqTimeout = 300
cpf.Language = "en-US"
credential := common.NewCredential(secretId, secretKey)
client, err = cvm.NewClient(credential, region, cpf)
return
}
// NewVpcClient returns a new vpc client
func NewVpcClient(secretId, secretKey, region string) (client *vpc.Client, err error) {
cpf := profile.NewClientProfile()
cpf.HttpProfile.ReqMethod = "POST"
cpf.HttpProfile.ReqTimeout = 300
cpf.Language = "en-US"
credential := common.NewCredential(secretId, secretKey)
client, err = vpc.NewClient(credential, region, cpf)
return
}
// CheckResourceIdFormat check resource id format
func CheckResourceIdFormat(resource string, id string) bool {
regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource))
return regex.MatchString(id)
}
// SSHHost returns a function that can be given to the SSH communicator
func SSHHost(pubilcIp bool) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
instance := state.Get("instance").(*cvm.Instance)
if pubilcIp {
return *instance.PublicIpAddresses[0], nil
} else {
return *instance.PrivateIpAddresses[0], nil
}
}
}
// Retry do retry on api request
func Retry(ctx context.Context, fn func(context.Context) error) error {
return retry.Config{
Tries: 60,
ShouldRetry: func(err error) bool {
e, ok := err.(*errors.TencentCloudSDKError)
if !ok {
return false
}
if e.Code == "ClientError.NetworkError" || e.Code == "ClientError.HttpStatusCodeError" ||
e.Code == "InvalidKeyPair.NotSupported" || e.Code == "InvalidParameterValue.KeyPairNotSupported" ||
e.Code == "InvalidInstance.NotSupported" || e.Code == "OperationDenied.InstanceOperationInProgress" ||
strings.Contains(e.Code, "RequestLimitExceeded") || strings.Contains(e.Code, "InternalError") ||
strings.Contains(e.Code, "ResourceInUse") || strings.Contains(e.Code, "ResourceBusy") {
return true
}
return false
},
RetryDelay: (&retry.Backoff{
InitialBackoff: 1 * time.Second,
MaxBackoff: 5 * time.Second,
Multiplier: 2,
}).Linear,
}.Run(ctx, fn)
}
// SayClean tell you clean module message
func SayClean(state multistep.StateBag, module string) {
_, halted := state.GetOk(multistep.StateHalted)
_, cancelled := state.GetOk(multistep.StateCancelled)
if halted {
Say(state, fmt.Sprintf("Deleting %s because of error...", module), "")
} else if cancelled {
Say(state, fmt.Sprintf("Deleting %s because of cancellation...", module), "")
} else {
Say(state, fmt.Sprintf("Cleaning up %s...", module), "")
}
}
// Say tell you a message
func Say(state multistep.StateBag, message, prefix string) {
if prefix != "" {
message = fmt.Sprintf("%s: %s", prefix, message)
}
if strings.HasPrefix(message, "Trying to") {
message += "..."
}
ui := state.Get("ui").(packersdk.Ui)
ui.Say(message)
}
// Message print a message
func Message(state multistep.StateBag, message, prefix string) {
if prefix != "" {
message = fmt.Sprintf("%s: %s", prefix, message)
}
ui := state.Get("ui").(packersdk.Ui)
ui.Message(message)
}
// Error print error message
func Error(state multistep.StateBag, err error, prefix string) {
if prefix != "" {
err = fmt.Errorf("%s: %s", prefix, err)
}
ui := state.Get("ui").(packersdk.Ui)
ui.Error(err.Error())
}
// Halt print error message and exit
func Halt(state multistep.StateBag, err error, prefix string) multistep.StepAction {
Error(state, err, prefix)
state.Put("error", err)
return multistep.ActionHalt
}

View File

@ -1,79 +0,0 @@
//go:generate packer-sdc struct-markdown
package cvm
import (
"fmt"
"unicode/utf8"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type TencentCloudImageConfig struct {
// The name you want to create your customize image,
// it should be composed of no more than 60 characters, of letters, numbers
// or minus sign.
ImageName string `mapstructure:"image_name" required:"true"`
// Image description.
ImageDescription string `mapstructure:"image_description" required:"false"`
// Whether shutdown cvm to create Image. Default value is
// false.
Reboot bool `mapstructure:"reboot" required:"false"`
// Whether to force power off cvm when create image.
// Default value is false.
ForcePoweroff bool `mapstructure:"force_poweroff" required:"false"`
// Whether enable Sysprep during creating windows image.
Sysprep bool `mapstructure:"sysprep" required:"false"`
ImageForceDelete bool `mapstructure:"image_force_delete"`
// regions that will be copied to after
// your image created.
ImageCopyRegions []string `mapstructure:"image_copy_regions" required:"false"`
// accounts that will be shared to
// after your image created.
ImageShareAccounts []string `mapstructure:"image_share_accounts" required:"false"`
// Do not check region and zone when validate.
SkipValidation bool `mapstructure:"skip_region_validation" required:"false"`
}
func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
cf.ForcePoweroff = true
if cf.ImageName == "" {
errs = append(errs, fmt.Errorf("image_name must be specified"))
} else if utf8.RuneCountInString(cf.ImageName) > 60 {
errs = append(errs, fmt.Errorf("image_name length should not exceed 60 characters"))
}
if utf8.RuneCountInString(cf.ImageDescription) > 60 {
errs = append(errs, fmt.Errorf("image_description length should not exceed 60 characters"))
}
if len(cf.ImageCopyRegions) > 0 {
regionSet := make(map[string]struct{})
regions := make([]string, 0, len(cf.ImageCopyRegions))
for _, region := range cf.ImageCopyRegions {
if _, ok := regionSet[region]; ok {
continue
}
regionSet[region] = struct{}{}
if !cf.SkipValidation {
if err := validRegion(region); err != nil {
errs = append(errs, err)
continue
}
}
regions = append(regions, region)
}
cf.ImageCopyRegions = regions
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@ -1,34 +0,0 @@
package cvm
import "testing"
func TestTencentCloudImageConfig_Prepare(t *testing.T) {
cf := &TencentCloudImageConfig{
ImageName: "foo",
}
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %v", err)
}
cf.ImageName = "foo.:"
if err := cf.Prepare(nil); err != nil {
t.Fatal("shouldn't have error")
}
cf.ImageName = "foo"
cf.ImageCopyRegions = []string{"ap-guangzhou", "ap-hongkong"}
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %v", err)
}
cf.ImageCopyRegions = []string{"unknown"}
if err := cf.Prepare(nil); err == nil {
t.Fatal("should have err")
}
cf.SkipValidation = true
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err:%v", err)
}
}

View File

@ -1,208 +0,0 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type tencentCloudDataDisk
package cvm
import (
"fmt"
"os"
"strings"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-sdk/uuid"
"github.com/pkg/errors"
)
type tencentCloudDataDisk struct {
DiskType string `mapstructure:"disk_type"`
DiskSize int64 `mapstructure:"disk_size"`
SnapshotId string `mapstructure:"disk_snapshot_id"`
}
type TencentCloudRunConfig struct {
// Whether allocate public ip to your cvm.
// Default value is false.
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address" required:"false"`
// The base image id of Image you want to create
// 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.
SourceImageName string `mapstructure:"source_image_name" required:"false"`
// The instance type your cvm will be launched by.
// You should reference Instace Type
// for parameter taking.
InstanceType string `mapstructure:"instance_type" required:"true"`
// Instance name.
InstanceName string `mapstructure:"instance_name" required:"false"`
// Root disk type your cvm will be launched by, default is `CLOUD_PREMIUM`. you could
// reference Disk Type
// for parameter taking.
DiskType string `mapstructure:"disk_type" required:"false"`
// Root disk size your cvm will be launched by. values range(in GB):
DiskSize int64 `mapstructure:"disk_size" required:"false"`
// Add one or more data disks to the instance before creating the image.
// Note that if the source image has data disk snapshots, this argument
// will be ignored, and the running instance will use source image data
// disk settings, in such case, `disk_type` argument will be used as disk
// 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_size` - Size of the data disk.
// - `disk_snapshot_id` - Id of the snapshot for a data disk.
DataDisks []tencentCloudDataDisk `mapstructure:"data_disks"`
// Specify vpc your cvm will be launched by.
VpcId string `mapstructure:"vpc_id" required:"false"`
// Specify vpc name you will create. if vpc_id is not set, packer will
// create a vpc for you named this parameter.
VpcName string `mapstructure:"vpc_name" required:"false"`
VpcIp string `mapstructure:"vpc_ip"`
// Specify subnet your cvm will be launched by.
SubnetId string `mapstructure:"subnet_id" required:"false"`
// Specify subnet name you will create. if subnet_id is not set, packer will
// create a subnet for you named this parameter.
SubnetName string `mapstructure:"subnet_name" required:"false"`
// Specify cider block of the vpc you will create if vpc_id not set
CidrBlock string `mapstructure:"cidr_block" required:"false"` // 10.0.0.0/16(default), 172.16.0.0/12, 192.168.0.0/16
// Specify cider block of the subnet you will create if
// subnet_id not set
SubnectCidrBlock string `mapstructure:"subnect_cidr_block" required:"false"`
InternetChargeType string `mapstructure:"internet_charge_type"`
// Max bandwidth out your cvm will be launched by(in MB).
// values can be set between 1 ~ 100.
InternetMaxBandwidthOut int64 `mapstructure:"internet_max_bandwidth_out" required:"false"`
// Specify securitygroup your cvm will be launched by.
SecurityGroupId string `mapstructure:"security_group_id" required:"false"`
// Specify security name you will create if security_group_id not set.
SecurityGroupName string `mapstructure:"security_group_name" required:"false"`
// userdata.
UserData string `mapstructure:"user_data" required:"false"`
// userdata file.
UserDataFile string `mapstructure:"user_data_file" required:"false"`
// host name.
HostName string `mapstructure:"host_name" required:"false"`
// Key/value pair tags to apply to the instance that is *launched* to
// create the image. These tags are *not* applied to the resulting image.
RunTags map[string]string `mapstructure:"run_tags" required:"false"`
// Same as [`run_tags`](#run_tags) but defined as a singular repeatable
// block containing a `key` and a `value` field. In HCL2 mode the
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
// will allow you to create those programatically.
RunTag config.KeyValues `mapstructure:"run_tag" required:"false"`
// Communicator settings
Comm communicator.Config `mapstructure:",squash"`
SSHPrivateIp bool `mapstructure:"ssh_private_ip"`
}
var ValidCBSType = []string{
"LOCAL_BASIC", "LOCAL_SSD", "CLOUD_BASIC", "CLOUD_SSD", "CLOUD_PREMIUM",
}
func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error {
packerId := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()[:8])
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
cf.Comm.SSHTemporaryKeyPairName = packerId
}
errs := cf.Comm.Prepare(ctx)
if cf.SourceImageId == "" && cf.SourceImageName == "" {
errs = append(errs, errors.New("source_image_id or source_image_name must be specified"))
}
if cf.SourceImageId != "" && !CheckResourceIdFormat("img", cf.SourceImageId) {
errs = append(errs, errors.New("source_image_id wrong format"))
}
if cf.InstanceType == "" {
errs = append(errs, errors.New("instance_type must be specified"))
}
if cf.UserData != "" && cf.UserDataFile != "" {
errs = append(errs, errors.New("only one of user_data or user_data_file can be specified"))
} else if cf.UserDataFile != "" {
if _, err := os.Stat(cf.UserDataFile); err != nil {
errs = append(errs, errors.New("user_data_file not exist"))
}
}
if (cf.VpcId != "" || cf.CidrBlock != "") && cf.SubnetId == "" && cf.SubnectCidrBlock == "" {
errs = append(errs, errors.New("if vpc cidr_block is specified, then "+
"subnet_cidr_block must also be specified."))
}
if cf.VpcId == "" {
if cf.VpcName == "" {
cf.VpcName = packerId
}
if cf.CidrBlock == "" {
cf.CidrBlock = "10.0.0.0/16"
}
if cf.SubnetId != "" {
errs = append(errs, errors.New("can't set subnet_id without set vpc_id"))
}
}
if cf.SubnetId == "" {
if cf.SubnetName == "" {
cf.SubnetName = packerId
}
if cf.SubnectCidrBlock == "" {
cf.SubnectCidrBlock = "10.0.8.0/24"
}
}
if cf.SecurityGroupId == "" && cf.SecurityGroupName == "" {
cf.SecurityGroupName = packerId
}
if cf.DiskType != "" && !checkDiskType(cf.DiskType) {
errs = append(errs, errors.New(fmt.Sprintf("specified disk_type(%s) is invalid", cf.DiskType)))
} else if cf.DiskType == "" {
cf.DiskType = "CLOUD_PREMIUM"
}
if cf.DiskSize <= 0 {
cf.DiskSize = 50
}
if cf.AssociatePublicIpAddress && cf.InternetMaxBandwidthOut <= 0 {
cf.InternetMaxBandwidthOut = 1
}
if cf.InstanceName == "" {
cf.InstanceName = packerId
}
if cf.HostName == "" {
cf.HostName = cf.InstanceName
}
if len(cf.HostName) > 15 {
cf.HostName = cf.HostName[:15]
}
cf.HostName = strings.Replace(cf.HostName, "_", "-", -1)
if cf.RunTags == nil {
cf.RunTags = make(map[string]string)
}
errs = append(errs, cf.RunTag.CopyOn(&cf.RunTags)...)
return errs
}
func checkDiskType(diskType string) bool {
for _, valid := range ValidCBSType {
if valid == diskType {
return true
}
}
return false
}

View File

@ -1,35 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package cvm
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlattencentCloudDataDisk is an auto-generated flat version of tencentCloudDataDisk.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlattencentCloudDataDisk struct {
DiskType *string `mapstructure:"disk_type" cty:"disk_type" hcl:"disk_type"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
SnapshotId *string `mapstructure:"disk_snapshot_id" cty:"disk_snapshot_id" hcl:"disk_snapshot_id"`
}
// FlatMapstructure returns a new FlattencentCloudDataDisk.
// FlattencentCloudDataDisk is an auto-generated flat version of tencentCloudDataDisk.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*tencentCloudDataDisk) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlattencentCloudDataDisk)
}
// HCL2Spec returns the hcl spec of a tencentCloudDataDisk.
// This spec is used by HCL to read the fields of tencentCloudDataDisk.
// The decoded values from this spec will then be applied to a FlattencentCloudDataDisk.
func (*FlattencentCloudDataDisk) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"disk_snapshot_id": &hcldec.AttrSpec{Name: "disk_snapshot_id", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,135 +0,0 @@
package cvm
import (
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer-plugin-sdk/communicator"
)
func testConfig() *TencentCloudRunConfig {
return &TencentCloudRunConfig{
SourceImageId: "img-qwer1234",
InstanceType: "S3.SMALL2",
Comm: communicator.Config{
SSH: communicator.SSH{
SSHUsername: "tencentcloud",
},
},
}
}
func TestTencentCloudRunConfig_Prepare(t *testing.T) {
cf := testConfig()
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %v", err)
}
cf.InstanceType = ""
if err := cf.Prepare(nil); err == nil {
t.Fatal("should have err")
}
cf.InstanceType = "S3.SMALL2"
cf.SourceImageId = ""
if err := cf.Prepare(nil); err == nil {
t.Fatal("should have err")
}
cf.SourceImageId = "img-qwer1234"
cf.Comm.SSHPort = 0
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %v", err)
}
if cf.Comm.SSHPort != 22 {
t.Fatalf("invalid ssh port value: %v", cf.Comm.SSHPort)
}
cf.Comm.SSHPort = 44
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %v", err)
}
if cf.Comm.SSHPort != 44 {
t.Fatalf("invalid ssh port value: %v", cf.Comm.SSHPort)
}
}
func TestTencentCloudRunConfigPrepare_UserData(t *testing.T) {
cf := testConfig()
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("new temp file failed: %v", err)
}
defer os.Remove(tf.Name())
defer tf.Close()
cf.UserData = "text user_data"
cf.UserDataFile = tf.Name()
if err := cf.Prepare(nil); err == nil {
t.Fatal("should have error")
}
}
func TestTencentCloudRunConfigPrepare_UserDataFile(t *testing.T) {
cf := testConfig()
cf.UserDataFile = "not-exist-file"
if err := cf.Prepare(nil); err == nil {
t.Fatal("should have error")
}
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("new temp file failed: %v", err)
}
defer os.Remove(tf.Name())
defer tf.Close()
cf.UserDataFile = tf.Name()
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have error: %v", err)
}
}
func TestTencentCloudRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
cf := testConfig()
cf.Comm.SSHTemporaryKeyPairName = ""
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have error: %v", err)
}
if cf.Comm.SSHTemporaryKeyPairName == "" {
t.Fatal("invalid ssh key pair value")
}
cf.Comm.SSHTemporaryKeyPairName = "ssh-key-123"
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have error: %v", err)
}
if cf.Comm.SSHTemporaryKeyPairName != "ssh-key-123" {
t.Fatalf("invalid ssh key pair value: %v", cf.Comm.SSHTemporaryKeyPairName)
}
}
func TestTencentCloudRunConfigPrepare_SSHPrivateIp(t *testing.T) {
cf := testConfig()
if cf.SSHPrivateIp != false {
t.Fatalf("invalid ssh_private_ip value: %v", cf.SSHPrivateIp)
}
cf.SSHPrivateIp = true
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have error: %v", err)
}
if cf.SSHPrivateIp != true {
t.Fatalf("invalud ssh_private_ip value: %v", cf.SSHPrivateIp)
}
}

View File

@ -1,66 +0,0 @@
package cvm
import (
"context"
"fmt"
"regexp"
"github.com/hashicorp/packer-plugin-sdk/multistep"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepCheckSourceImage struct {
sourceImageId string
}
func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
var (
imageNameRegex *regexp.Regexp
err error
)
config := state.Get("config").(*Config)
client := state.Get("cvm_client").(*cvm.Client)
Say(state, config.SourceImageId, "Trying to check source image")
req := cvm.NewDescribeImagesRequest()
req.InstanceType = &config.InstanceType
if config.SourceImageId != "" {
req.ImageIds = []*string{&config.SourceImageId}
} else {
imageNameRegex, err = regexp.Compile(config.SourceImageName)
if err != nil {
return Halt(state, fmt.Errorf("regex compilation error"), "Bad input")
}
}
var resp *cvm.DescribeImagesResponse
err = Retry(ctx, func(ctx context.Context) error {
var err error
resp, err = client.DescribeImages(req)
return err
})
if err != nil {
return Halt(state, err, "Failed to get source image info")
}
if *resp.Response.TotalCount > 0 {
images := resp.Response.ImageSet
if imageNameRegex != nil {
for _, image := range images {
if imageNameRegex.MatchString(*image.ImageName) {
state.Put("source_image", image)
Message(state, *image.ImageName, "Image found")
return multistep.ActionContinue
}
}
} else {
state.Put("source_image", images[0])
Message(state, *resp.Response.ImageSet[0].ImageName, "Image found")
return multistep.ActionContinue
}
}
return Halt(state, fmt.Errorf("No image found under current instance_type(%s) restriction", config.InstanceType), "")
}
func (s *stepCheckSourceImage) Cleanup(bag multistep.StateBag) {}

View File

@ -1,120 +0,0 @@
package cvm
import (
"context"
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepConfigKeyPair struct {
Debug bool
Comm *communicator.Config
DebugKeyPath string
keyID string
}
func (s *stepConfigKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
if s.Comm.SSHPrivateKeyFile != "" {
Say(state, "Using existing SSH private key", "")
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
return Halt(state, err, fmt.Sprintf("Failed to load configured private key(%s)", s.Comm.SSHPrivateKeyFile))
}
s.Comm.SSHPrivateKey = privateKeyBytes
Message(state, fmt.Sprintf("Loaded %d bytes private key data", len(s.Comm.SSHPrivateKey)), "")
return multistep.ActionContinue
}
if s.Comm.SSHAgentAuth {
if s.Comm.SSHKeyPairName == "" {
Say(state, "Using SSH agent with key pair in source image", "")
return multistep.ActionContinue
}
Say(state, fmt.Sprintf("Using SSH agent with exists key pair(%s)", s.Comm.SSHKeyPairName), "")
return multistep.ActionContinue
}
if s.Comm.SSHTemporaryKeyPairName == "" {
Say(state, "Not to use temporary keypair", "")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
Say(state, s.Comm.SSHTemporaryKeyPairName, "Trying to create a new keypair")
req := cvm.NewCreateKeyPairRequest()
req.KeyName = &s.Comm.SSHTemporaryKeyPairName
defaultProjectId := int64(0)
req.ProjectId = &defaultProjectId
var resp *cvm.CreateKeyPairResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = client.CreateKeyPair(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to create keypair")
}
// set keyId to delete when Cleanup
s.keyID = *resp.Response.KeyPair.KeyId
state.Put("temporary_key_pair_id", s.keyID)
Message(state, s.keyID, "Keypair created")
s.Comm.SSHKeyPairName = *resp.Response.KeyPair.KeyId
s.Comm.SSHPrivateKey = []byte(*resp.Response.KeyPair.PrivateKey)
if s.Debug {
Message(state, fmt.Sprintf("Saving temporary key to %s for debug purposes", s.DebugKeyPath), "")
f, err := os.Create(s.DebugKeyPath)
if err != nil {
return Halt(state, err, "Failed to saving debug key file")
}
defer f.Close()
if _, err := f.Write([]byte(*resp.Response.KeyPair.PrivateKey)); err != nil {
return Halt(state, err, "Failed to writing debug key file")
}
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
return Halt(state, err, "Failed to chmod debug key file")
}
}
}
return multistep.ActionContinue
}
func (s *stepConfigKeyPair) Cleanup(state multistep.StateBag) {
if s.Comm.SSHPrivateKeyFile != "" || (s.Comm.SSHKeyPairName == "" && s.keyID == "") {
return
}
ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
SayClean(state, "keypair")
req := cvm.NewDeleteKeyPairsRequest()
req.KeyIds = []*string{&s.keyID}
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.DeleteKeyPairs(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to delete keypair(%s), please delete it manually", s.keyID))
}
if s.Debug {
if err := os.Remove(s.DebugKeyPath); err != nil {
Error(state, err, fmt.Sprintf("Failed to delete debug key file(%s), please delete it manually", s.DebugKeyPath))
}
}
}

View File

@ -1,128 +0,0 @@
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
type stepConfigSecurityGroup struct {
SecurityGroupId string
SecurityGroupName string
Description string
isCreate bool
}
func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
if len(s.SecurityGroupId) != 0 {
Say(state, s.SecurityGroupId, "Trying to use existing securitygroup")
req := vpc.NewDescribeSecurityGroupsRequest()
req.SecurityGroupIds = []*string{&s.SecurityGroupId}
var resp *vpc.DescribeSecurityGroupsResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.DescribeSecurityGroups(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to get securitygroup info")
}
if *resp.Response.TotalCount > 0 {
s.isCreate = false
state.Put("security_group_id", s.SecurityGroupId)
Message(state, *resp.Response.SecurityGroupSet[0].SecurityGroupName, "Securitygroup found")
return multistep.ActionContinue
}
return Halt(state, fmt.Errorf("The specified securitygroup(%s) does not exists", s.SecurityGroupId), "")
}
Say(state, "Trying to create a new securitygroup", "")
req := vpc.NewCreateSecurityGroupRequest()
req.GroupName = &s.SecurityGroupName
req.GroupDescription = &s.Description
var resp *vpc.CreateSecurityGroupResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.CreateSecurityGroup(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to create securitygroup")
}
s.isCreate = true
s.SecurityGroupId = *resp.Response.SecurityGroup.SecurityGroupId
state.Put("security_group_id", s.SecurityGroupId)
Message(state, s.SecurityGroupId, "Securitygroup created")
// bind securitygroup ingress police
Say(state, "Trying to create securitygroup polices", "")
pReq := vpc.NewCreateSecurityGroupPoliciesRequest()
ACCEPT := "ACCEPT"
DEFAULT_CIDR := "0.0.0.0/0"
pReq.SecurityGroupId = &s.SecurityGroupId
pReq.SecurityGroupPolicySet = &vpc.SecurityGroupPolicySet{
Ingress: []*vpc.SecurityGroupPolicy{
{
CidrBlock: &DEFAULT_CIDR,
Action: &ACCEPT,
},
},
}
err = Retry(ctx, func(ctx context.Context) error {
_, e := vpcClient.CreateSecurityGroupPolicies(pReq)
return e
})
if err != nil {
return Halt(state, err, "Failed to create securitygroup polices")
}
// bind securitygroup engress police
pReq = vpc.NewCreateSecurityGroupPoliciesRequest()
pReq.SecurityGroupId = &s.SecurityGroupId
pReq.SecurityGroupPolicySet = &vpc.SecurityGroupPolicySet{
Egress: []*vpc.SecurityGroupPolicy{
{
CidrBlock: &DEFAULT_CIDR,
Action: &ACCEPT,
},
},
}
err = Retry(ctx, func(ctx context.Context) error {
_, e := vpcClient.CreateSecurityGroupPolicies(pReq)
return e
})
if err != nil {
return Halt(state, err, "Failed to create securitygroup polices")
}
Message(state, "Securitygroup polices created", "")
return multistep.ActionContinue
}
func (s *stepConfigSecurityGroup) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
SayClean(state, "securitygroup")
req := vpc.NewDeleteSecurityGroupRequest()
req.SecurityGroupId = &s.SecurityGroupId
err := Retry(ctx, func(ctx context.Context) error {
_, e := vpcClient.DeleteSecurityGroup(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to delete securitygroup(%s), please delete it manually", s.SecurityGroupId))
}
}

View File

@ -1,93 +0,0 @@
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
type stepConfigSubnet struct {
SubnetId string
SubnetCidrBlock string
SubnetName string
Zone string
isCreate bool
}
func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
vpcId := state.Get("vpc_id").(string)
if len(s.SubnetId) != 0 {
Say(state, s.SubnetId, "Trying to use existing subnet")
req := vpc.NewDescribeSubnetsRequest()
req.SubnetIds = []*string{&s.SubnetId}
var resp *vpc.DescribeSubnetsResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.DescribeSubnets(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to get subnet info")
}
if *resp.Response.TotalCount > 0 {
s.isCreate = false
if *resp.Response.SubnetSet[0].VpcId != vpcId {
return Halt(state, fmt.Errorf("The specified subnet(%s) does not belong to the specified vpc(%s)", s.SubnetId, vpcId), "")
}
state.Put("subnet_id", *resp.Response.SubnetSet[0].SubnetId)
Message(state, *resp.Response.SubnetSet[0].SubnetName, "Subnet found")
return multistep.ActionContinue
}
return Halt(state, fmt.Errorf("The specified subnet(%s) does not exist", s.SubnetId), "")
}
Say(state, "Trying to create a new subnet", "")
req := vpc.NewCreateSubnetRequest()
req.VpcId = &vpcId
req.SubnetName = &s.SubnetName
req.CidrBlock = &s.SubnetCidrBlock
req.Zone = &s.Zone
var resp *vpc.CreateSubnetResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.CreateSubnet(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to create subnet")
}
s.isCreate = true
s.SubnetId = *resp.Response.Subnet.SubnetId
state.Put("subnet_id", s.SubnetId)
Message(state, s.SubnetId, "Subnet created")
return multistep.ActionContinue
}
func (s *stepConfigSubnet) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
SayClean(state, "subnet")
req := vpc.NewDeleteSubnetRequest()
req.SubnetId = &s.SubnetId
err := Retry(ctx, func(ctx context.Context) error {
_, e := vpcClient.DeleteSubnet(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to delete subnet(%s), please delete it manually", s.SubnetId))
}
}

View File

@ -1,85 +0,0 @@
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
type stepConfigVPC struct {
VpcId string
CidrBlock string
VpcName string
isCreate bool
}
func (s *stepConfigVPC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
if len(s.VpcId) != 0 {
Say(state, s.VpcId, "Trying to use existing vpc")
req := vpc.NewDescribeVpcsRequest()
req.VpcIds = []*string{&s.VpcId}
var resp *vpc.DescribeVpcsResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.DescribeVpcs(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to get vpc info")
}
if *resp.Response.TotalCount > 0 {
s.isCreate = false
state.Put("vpc_id", *resp.Response.VpcSet[0].VpcId)
Message(state, *resp.Response.VpcSet[0].VpcName, "Vpc found")
return multistep.ActionContinue
}
return Halt(state, fmt.Errorf("The specified vpc(%s) does not exist", s.VpcId), "")
}
Say(state, "Trying to create a new vpc", "")
req := vpc.NewCreateVpcRequest()
req.VpcName = &s.VpcName
req.CidrBlock = &s.CidrBlock
var resp *vpc.CreateVpcResponse
err := Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = vpcClient.CreateVpc(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to create vpc")
}
s.isCreate = true
s.VpcId = *resp.Response.Vpc.VpcId
state.Put("vpc_id", s.VpcId)
Message(state, s.VpcId, "Vpc created")
return multistep.ActionContinue
}
func (s *stepConfigVPC) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
SayClean(state, "vpc")
req := vpc.NewDeleteVpcRequest()
req.VpcId = &s.VpcId
err := Retry(ctx, func(ctx context.Context) error {
_, e := vpcClient.DeleteVpc(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to delete vpc(%s), please delete it manually", s.VpcId))
}
}

View File

@ -1,81 +0,0 @@
package cvm
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepCopyImage struct {
DesinationRegions []string
SourceRegion string
}
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) {
return multistep.ActionContinue
}
config := state.Get("config").(*Config)
client := state.Get("cvm_client").(*cvm.Client)
imageId := state.Get("image").(*cvm.Image).ImageId
Say(state, strings.Join(s.DesinationRegions, ","), "Trying to copy image to")
req := cvm.NewSyncImagesRequest()
req.ImageIds = []*string{imageId}
copyRegions := make([]*string, 0, len(s.DesinationRegions))
for _, region := range s.DesinationRegions {
if region != s.SourceRegion {
copyRegions = append(copyRegions, common.StringPtr(region))
}
}
req.DestinationRegions = copyRegions
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.SyncImages(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to copy image")
}
Message(state, "Waiting for image ready", "")
tencentCloudImages := state.Get("tencentcloudimages").(map[string]string)
for _, region := range req.DestinationRegions {
rc, err := NewCvmClient(config.SecretId, config.SecretKey, *region)
if err != nil {
return Halt(state, err, "Failed to init client")
}
err = WaitForImageReady(ctx, rc, config.ImageName, "NORMAL", 1800)
if err != nil {
return Halt(state, err, "Failed to wait for image ready")
}
image, err := GetImageByName(ctx, rc, config.ImageName)
if err != nil {
return Halt(state, err, "Failed to get image")
}
if image == nil {
return Halt(state, err, "Failed to wait for image ready")
}
tencentCloudImages[*region] = *image.ImageId
Message(state, fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.SourceRegion, *imageId, *region, *image.ImageId), "")
}
state.Put("tencentcloudimages", tencentCloudImages)
Message(state, "Image copied", "")
return multistep.ActionContinue
}
func (s *stepCopyImage) Cleanup(state multistep.StateBag) {}

View File

@ -1,111 +0,0 @@
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepCreateImage struct {
imageId string
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
config := state.Get("config").(*Config)
instance := state.Get("instance").(*cvm.Instance)
Say(state, config.ImageName, "Trying to create a new image")
req := cvm.NewCreateImageRequest()
req.ImageName = &config.ImageName
req.ImageDescription = &config.ImageDescription
req.InstanceId = instance.InstanceId
// TODO: We should allow user to specify which data disk should be
// included into created image.
var dataDiskIds []*string
for _, disk := range instance.DataDisks {
dataDiskIds = append(dataDiskIds, disk.DiskId)
}
if len(dataDiskIds) > 0 {
req.DataDiskIds = dataDiskIds
}
True := "True"
False := "False"
if config.ForcePoweroff {
req.ForcePoweroff = &True
} else {
req.ForcePoweroff = &False
}
if config.Sysprep {
req.Sysprep = &True
} else {
req.Sysprep = &False
}
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.CreateImage(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to create image")
}
Message(state, "Waiting for image ready", "")
err = WaitForImageReady(ctx, client, config.ImageName, "NORMAL", 3600)
if err != nil {
return Halt(state, err, "Failed to wait for image ready")
}
image, err := GetImageByName(ctx, client, config.ImageName)
if err != nil {
return Halt(state, err, "Failed to get image")
}
if image == nil {
return Halt(state, fmt.Errorf("No image return"), "Failed to crate image")
}
s.imageId = *image.ImageId
state.Put("image", image)
Message(state, s.imageId, "Image created")
tencentCloudImages := make(map[string]string)
tencentCloudImages[config.Region] = s.imageId
state.Put("tencentcloudimages", tencentCloudImages)
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
if s.imageId == "" {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
SayClean(state, "image")
req := cvm.NewDeleteImagesRequest()
req.ImageIds = []*string{&s.imageId}
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.DeleteImages(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to delete image(%s), please delete it manually", s.imageId))
}
}

View File

@ -1,49 +0,0 @@
package cvm
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepDetachTempKeyPair struct {
}
func (s *stepDetachTempKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
if _, ok := state.GetOk("temporary_key_pair_id"); !ok {
return multistep.ActionContinue
}
keyId := state.Get("temporary_key_pair_id").(string)
instance := state.Get("instance").(*cvm.Instance)
Say(state, keyId, "Trying to detach keypair")
req := cvm.NewDisassociateInstancesKeyPairsRequest()
req.KeyIds = []*string{&keyId}
req.InstanceIds = []*string{instance.InstanceId}
req.ForceStop = common.BoolPtr(true)
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.DisassociateInstancesKeyPairs(req)
return e
})
if err != nil {
return Halt(state, err, "Fail to detach keypair from instance")
}
Message(state, "Waiting for keypair detached", "")
err = WaitForInstance(ctx, client, *instance.InstanceId, "RUNNING", 1800)
if err != nil {
return Halt(state, err, "Failed to wait for keypair detached")
}
Message(state, "Keypair detached", "")
return multistep.ActionContinue
}
func (s *stepDetachTempKeyPair) Cleanup(state multistep.StateBag) {}

View File

@ -1,34 +0,0 @@
package cvm
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepPreValidate struct {
}
func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
client := state.Get("cvm_client").(*cvm.Client)
Say(state, config.ImageName, "Trying to check image name")
image, err := GetImageByName(ctx, client, config.ImageName)
if err != nil {
return Halt(state, err, "Failed to get images info")
}
if image != nil {
return Halt(state, fmt.Errorf("Image name %s has exists", config.ImageName), "")
}
Message(state, "useable", "Image name")
return multistep.ActionContinue
}
func (s *stepPreValidate) Cleanup(multistep.StateBag) {}

View File

@ -1,222 +0,0 @@
package cvm
import (
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepRunInstance struct {
InstanceType string
UserData string
UserDataFile string
instanceId string
ZoneId string
InstanceName string
DiskType string
DiskSize int64
HostName string
InternetMaxBandwidthOut int64
AssociatePublicIpAddress bool
Tags map[string]string
DataDisks []tencentCloudDataDisk
}
func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
config := state.Get("config").(*Config)
source_image := state.Get("source_image").(*cvm.Image)
vpc_id := state.Get("vpc_id").(string)
subnet_id := state.Get("subnet_id").(string)
security_group_id := state.Get("security_group_id").(string)
password := config.Comm.SSHPassword
if password == "" && config.Comm.WinRMPassword != "" {
password = config.Comm.WinRMPassword
}
userData, err := s.getUserData(state)
if err != nil {
return Halt(state, err, "Failed to get user_data")
}
Say(state, "Trying to create a new instance", "")
// config RunInstances parameters
POSTPAID_BY_HOUR := "POSTPAID_BY_HOUR"
req := cvm.NewRunInstancesRequest()
if s.ZoneId != "" {
req.Placement = &cvm.Placement{
Zone: &s.ZoneId,
}
}
req.ImageId = source_image.ImageId
req.InstanceChargeType = &POSTPAID_BY_HOUR
req.InstanceType = &s.InstanceType
// TODO: Add check for system disk size, it should be larger than image system disk size.
req.SystemDisk = &cvm.SystemDisk{
DiskType: &s.DiskType,
DiskSize: &s.DiskSize,
}
// System disk snapshot is mandatory, so if there are additional data disks,
// length will be larger than 1.
if source_image.SnapshotSet != nil && len(source_image.SnapshotSet) > 1 {
Message(state, "Use source image snapshot data disks, ignore user data disk settings", "")
var dataDisks []*cvm.DataDisk
for _, snapshot := range source_image.SnapshotSet {
if *snapshot.DiskUsage == "DATA_DISK" {
var dataDisk cvm.DataDisk
// FIXME: Currently we have no way to get original disk type
// from data disk snapshots, and we don't allow user to overwrite
// snapshot settings, and we cannot guarantee a certain hard-coded type
// is not sold out, so here we use system disk type as a workaround.
//
// Eventually, we need to allow user to overwrite snapshot disk
// settings.
dataDisk.DiskType = &s.DiskType
dataDisk.DiskSize = snapshot.DiskSize
dataDisk.SnapshotId = snapshot.SnapshotId
dataDisks = append(dataDisks, &dataDisk)
}
}
req.DataDisks = dataDisks
} else {
var dataDisks []*cvm.DataDisk
for _, disk := range s.DataDisks {
var dataDisk cvm.DataDisk
dataDisk.DiskType = &disk.DiskType
dataDisk.DiskSize = &disk.DiskSize
if disk.SnapshotId != "" {
dataDisk.SnapshotId = &disk.SnapshotId
}
dataDisks = append(dataDisks, &dataDisk)
}
req.DataDisks = dataDisks
}
req.VirtualPrivateCloud = &cvm.VirtualPrivateCloud{
VpcId: &vpc_id,
SubnetId: &subnet_id,
}
TRAFFIC_POSTPAID_BY_HOUR := "TRAFFIC_POSTPAID_BY_HOUR"
if s.AssociatePublicIpAddress {
req.InternetAccessible = &cvm.InternetAccessible{
InternetChargeType: &TRAFFIC_POSTPAID_BY_HOUR,
InternetMaxBandwidthOut: &s.InternetMaxBandwidthOut,
}
}
req.InstanceName = &s.InstanceName
loginSettings := cvm.LoginSettings{}
if password != "" {
loginSettings.Password = &password
}
if config.Comm.SSHKeyPairName != "" {
loginSettings.KeyIds = []*string{&config.Comm.SSHKeyPairName}
}
req.LoginSettings = &loginSettings
req.SecurityGroupIds = []*string{&security_group_id}
req.ClientToken = &s.InstanceName
req.HostName = &s.HostName
req.UserData = &userData
var tags []*cvm.Tag
for k, v := range s.Tags {
tags = append(tags, &cvm.Tag{
Key: &k,
Value: &v,
})
}
resourceType := "instance"
if len(tags) > 0 {
req.TagSpecification = []*cvm.TagSpecification{
{
ResourceType: &resourceType,
Tags: tags,
},
}
}
var resp *cvm.RunInstancesResponse
err = Retry(ctx, func(ctx context.Context) error {
var e error
resp, e = client.RunInstances(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to run instance")
}
if len(resp.Response.InstanceIdSet) != 1 {
return Halt(state, fmt.Errorf("No instance return"), "Failed to run instance")
}
s.instanceId = *resp.Response.InstanceIdSet[0]
Message(state, "Waiting for instance ready", "")
err = WaitForInstance(ctx, client, s.instanceId, "RUNNING", 1800)
if err != nil {
return Halt(state, err, "Failed to wait for instance ready")
}
describeReq := cvm.NewDescribeInstancesRequest()
describeReq.InstanceIds = []*string{&s.instanceId}
var describeResp *cvm.DescribeInstancesResponse
err = Retry(ctx, func(ctx context.Context) error {
var e error
describeResp, e = client.DescribeInstances(describeReq)
return e
})
if err != nil {
return Halt(state, err, "Failed to wait for instance ready")
}
state.Put("instance", describeResp.Response.InstanceSet[0])
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_id", s.instanceId)
Message(state, s.instanceId, "Instance created")
return multistep.ActionContinue
}
func (s *stepRunInstance) getUserData(state multistep.StateBag) (string, error) {
userData := s.UserData
if userData == "" && s.UserDataFile != "" {
data, err := ioutil.ReadFile(s.UserDataFile)
if err != nil {
return "", err
}
userData = string(data)
}
userData = base64.StdEncoding.EncodeToString([]byte(userData))
log.Printf(fmt.Sprintf("[DEBUG]getUserData: user_data: %s", userData))
return userData, nil
}
func (s *stepRunInstance) Cleanup(state multistep.StateBag) {
if s.instanceId == "" {
return
}
ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
SayClean(state, "instance")
req := cvm.NewTerminateInstancesRequest()
req.InstanceIds = []*string{&s.instanceId}
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.TerminateInstances(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to terminate instance(%s), please delete it manually", s.instanceId))
}
}

View File

@ -1,76 +0,0 @@
package cvm
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepShareImage struct {
ShareAccounts []string
}
func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if len(s.ShareAccounts) == 0 {
return multistep.ActionContinue
}
client := state.Get("cvm_client").(*cvm.Client)
imageId := state.Get("image").(*cvm.Image).ImageId
Say(state, strings.Join(s.ShareAccounts, ","), "Trying to share image to")
req := cvm.NewModifyImageSharePermissionRequest()
req.ImageId = imageId
req.Permission = common.StringPtr("SHARE")
accounts := make([]*string, 0, len(s.ShareAccounts))
for _, account := range s.ShareAccounts {
accounts = append(accounts, common.StringPtr(account))
}
req.AccountIds = accounts
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.ModifyImageSharePermission(req)
return e
})
if err != nil {
return Halt(state, err, "Failed to share image")
}
Message(state, "Image shared", "")
return multistep.ActionContinue
}
func (s *stepShareImage) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
imageId := state.Get("image").(*cvm.Image).ImageId
SayClean(state, "image share")
req := cvm.NewModifyImageSharePermissionRequest()
req.ImageId = imageId
req.Permission = common.StringPtr("CANCEL")
accounts := make([]*string, 0, len(s.ShareAccounts))
for _, account := range s.ShareAccounts {
accounts = append(accounts, &account)
}
req.AccountIds = accounts
err := Retry(ctx, func(ctx context.Context) error {
_, e := client.ModifyImageSharePermission(req)
return e
})
if err != nil {
Error(state, err, fmt.Sprintf("Failed to cancel share image(%s), please delete it manually", *imageId))
}
}

View File

@ -1,40 +0,0 @@
{
"variables": {
"secret_id": "{{env `TENCENTCLOUD_ACCESS_KEY`}}",
"secret_key": "{{env `TENCENTCLOUD_SECRET_KEY`}}"
},
"builders": [
{
"type": "tencentcloud-cvm",
"secret_id": "{{user `secret_id`}}",
"secret_key": "{{user `secret_key`}}",
"region": "ap-guangzhou",
"zone": "ap-guangzhou-4",
"instance_type": "S4.SMALL1",
"source_image_id": "img-oikl1tzv",
"ssh_username": "root",
"image_name": "PackerTest",
"disk_type": "CLOUD_PREMIUM",
"packer_debug": true,
"associate_public_ip_address": true,
"data_disks": [
{
"disk_type": "CLOUD_PREMIUM",
"disk_size": 50
}
],
"run_tags": {
"good": "luck"
}
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 30",
"yum install redis.x86_64 -y"
]
}
]
}

View File

@ -1,34 +0,0 @@
{
"variables": {
"secret_id": "{{env `TENCENTCLOUD_ACCESS_KEY`}}",
"secret_key": "{{env `TENCENTCLOUD_SECRET_KEY`}}"
},
"builders": [
{
"type": "tencentcloud-cvm",
"secret_id": "{{user `secret_id`}}",
"secret_key": "{{user `secret_key`}}",
"region": "ap-guangzhou",
"zone": "ap-guangzhou-4",
"instance_type": "S4.SMALL1",
"source_image_id": "img-oikl1tzv",
"ssh_username": "root",
"image_name": "PackerTest",
"disk_type": "CLOUD_PREMIUM",
"packer_debug": true,
"associate_public_ip_address": true,
"run_tags": {
"good": "luck"
}
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 30",
"yum install redis.x86_64 -y"
]
}
]
}

View File

@ -1,42 +0,0 @@
{
"variables": {
"secret_id": "{{env `TENCENTCLOUD_ACCESS_KEY`}}",
"secret_key": "{{env `TENCENTCLOUD_SECRET_KEY`}}"
},
"builders": [
{
"type": "tencentcloud-cvm",
"secret_id": "{{user `secret_id`}}",
"secret_key": "{{user `secret_key`}}",
"region": "ap-guangzhou",
"zone": "ap-guangzhou-3",
"instance_type": "S3.SMALL1",
"source_image_id": "img-oikl1tzv",
"disk_type": "CLOUD_PREMIUM",
"vpc_id": "vpc-h70b6b49",
"subnet_id": "subnet-1uwh63so",
"internet_max_bandwidth_out": 2,
"security_group_id": "sg-nltpbqg1",
"ssh_username": "root",
"image_name": "packerTest",
"host_name": "packerTest",
"associate_public_ip_address": true,
"image_description": "centosPacker",
"image_copy_regions": [
"ap-beijing"
]
}
],
"provisioners": [
{
"execute_command": "echo '{{user `ssh_pass`}}' | {{ .Vars }} sudo -S -E sh '{{ .Path }}'",
"inline": [
"yum update -y",
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
],
"inline_shebang": "/bin/sh -x",
"type": "shell",
"skip_clean": true
}
]
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var TencentPluginVersion *version.PluginVersion
func init() {
TencentPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -20,7 +20,6 @@ import (
nullbuilder "github.com/hashicorp/packer/builder/null"
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
@ -54,7 +53,6 @@ var Builders = map[string]packersdk.Builder{
"null": new(nullbuilder.Builder),
"oneandone": new(oneandonebuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"yandex": new(yandexbuilder.Builder),
}

View File

@ -57,6 +57,7 @@ import (
puppetserverprovisioner "github.com/hashicorp/packer-plugin-puppet/provisioner/puppet-server"
qemubuilder "github.com/hashicorp/packer-plugin-qemu/builder/qemu"
scalewaybuilder "github.com/hashicorp/packer-plugin-scaleway/builder/scaleway"
tencentcloudcvmbuilder "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm"
tritonbuilder "github.com/hashicorp/packer-plugin-triton/builder/triton"
uclouduhostbuilder "github.com/hashicorp/packer-plugin-ucloud/builder/ucloud/uhost"
ucloudimportpostprocessor "github.com/hashicorp/packer-plugin-ucloud/post-processor/ucloud-import"
@ -113,6 +114,7 @@ var VendoredBuilders = map[string]packersdk.Builder{
"parallels-pvm": new(parallelspvmbuilder.Builder),
"qemu": new(qemubuilder.Builder),
"scaleway": new(scalewaybuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"triton": new(tritonbuilder.Builder),
"ucloud-uhost": new(uclouduhostbuilder.Builder),
"vagrant": new(vagrantbuilder.Builder),

3
go.mod
View File

@ -58,6 +58,7 @@ require (
github.com/hashicorp/packer-plugin-qemu v0.0.1
github.com/hashicorp/packer-plugin-scaleway v0.0.1
github.com/hashicorp/packer-plugin-sdk v0.2.0
github.com/hashicorp/packer-plugin-tencentcloud v0.0.1
github.com/hashicorp/packer-plugin-triton v0.0.0-20210421085122-768dd7c764d9
github.com/hashicorp/packer-plugin-ucloud v0.0.1
github.com/hashicorp/packer-plugin-vagrant v0.0.3
@ -74,12 +75,10 @@ require (
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/mitchellh/reflectwalk v1.0.0
github.com/pierrec/lz4 v2.0.5+incompatible
github.com/pkg/errors v0.9.1
github.com/posener/complete v1.2.3
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
github.com/shirou/gopsutil v3.21.1+incompatible
github.com/stretchr/testify v1.7.0
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible
github.com/ulikunitz/xz v0.5.6
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c

3
go.sum
View File

@ -584,6 +584,8 @@ github.com/hashicorp/packer-plugin-sdk v0.1.3/go.mod h1:xePpgQgQYv/bamiypx3hH9uk
github.com/hashicorp/packer-plugin-sdk v0.1.4/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ=
github.com/hashicorp/packer-plugin-sdk v0.2.0 h1:A4Dq7p4y1vscY4gMzp7GQaXyDJYYhP4ukp4fapPSOY4=
github.com/hashicorp/packer-plugin-sdk v0.2.0/go.mod h1:0DiOMEBldmB0HEhp0npFSSygC8bIvW43pphEgWkp2WU=
github.com/hashicorp/packer-plugin-tencentcloud v0.0.1 h1:DR7GETCzrK/DPFMUPbULIklCxwGhstbbz6pl+2S+UnM=
github.com/hashicorp/packer-plugin-tencentcloud v0.0.1/go.mod h1:FmdacMLvDKiT6OdMAc2x4LXtqu/soLApH3jF57SWOik=
github.com/hashicorp/packer-plugin-triton v0.0.0-20210421085122-768dd7c764d9 h1:No5oPI9Wa7FhTKkFJwI3hcfUVvEpgPC8QMcG9l/Vxzo=
github.com/hashicorp/packer-plugin-triton v0.0.0-20210421085122-768dd7c764d9/go.mod h1:XOAIiWYLbctBOsu41it/cL/ZjULAZ05YBhFm4H4M/lA=
github.com/hashicorp/packer-plugin-ucloud v0.0.1 h1:SC2F1BuXb6dKhY6fRdmAqTkuc17jlBIu/Ut0URJy8TU=
@ -886,6 +888,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tencentcloud/tencentcloud-sdk-go v1.0.140/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=

View File

@ -1,235 +0,0 @@
---
description: |
The cloudstack Packer builder is able to create new templates for use with
CloudStack. The builder takes either an ISO or an existing template as it's
source, runs any provisioning necessary on the instance after launching it and
then creates a new template from that instance.
page_title: CloudStack - Builders
---
# CloudStack Builder
Type: `cloudstack`
Artifact BuilderId: `packer.cloudstack`
The `cloudstack` Packer builder is able to create new templates for use with
[CloudStack](https://cloudstack.apache.org/). The builder takes either an ISO
or an existing template as it's source, runs any provisioning necessary on the
instance after launching it and then creates a new template from that instance.
The builder does _not_ manage templates. Once a template is created, it is up
to you to use it or delete it.
## Configuration Reference
There are many configuration options available for the builder. They are
segmented below into two categories: required and optional parameters. Within
each category, the available configuration keys are alphabetized.
In addition to the options listed here, a
[communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this
builder.
### Required:
- `api_url` (string) - The CloudStack API endpoint we will connect to. It can
also be specified via environment variable `CLOUDSTACK_API_URL`, if set.
- `api_key` (string) - The API key used to sign all API requests. It can also
be specified via environment variable `CLOUDSTACK_API_KEY`, if set.
- `network` (string) - The name or ID of the network to connect the instance
to.
- `secret_key` (string) - The secret key used to sign all API requests. It
can also be specified via environment variable `CLOUDSTACK_SECRET_KEY`, if
set.
- `service_offering` (string) - The name or ID of the service offering used
for the instance.
- `source_iso` (string) - The name or ID of an ISO that will be mounted
before booting the instance. This option is mutually exclusive with
`source_template`. When using `source_iso`, both `disk_offering` and
`hypervisor` are required.
- `source_template` (string) - The name or ID of the template used as base
template for the instance. This option is mutually exclusive with
`source_iso`.
- `template_os` (string) - The name or ID of the template OS for the new
template that will be created.
- `zone` (string) - The name or ID of the zone where the instance will be
created.
### Optional:
- `async_timeout` (number) - The time duration to wait for async calls to
finish. Defaults to 30m.
- `cidr_list` (array) - List of CIDR's that will have access to the new
instance. This is needed in order for any provisioners to be able to
connect to the instance. Defaults to `[ "0.0.0.0/0" ]`. Only required when
`use_local_ip_address` is `false`.
- `create_security_group` (boolean) - If `true` a temporary security group
will be created which allows traffic towards the instance from the
`cidr_list`. This option will be ignored if `security_groups` is also
defined. Requires `expunge` set to `true`. Defaults to `false`.
- `disk_offering` (string) - The name or ID of the disk offering used for the
instance. This option is only available (and also required) when using
`source_iso`.
- `disk_size` (number) - The size (in GB) of the root disk of the new
instance. This option is only available when using `source_template`.
- `expunge` (boolean) - Set to `true` to expunge the instance when it is
destroyed. Defaults to `false`.
- `http_get_only` (boolean) - Some cloud providers only allow HTTP GET calls
to their CloudStack API. If using such a provider, you need to set this to
`true` in order for the provider to only make GET calls and no POST calls.
- `hypervisor` (string) - The target hypervisor (e.g. `XenServer`, `KVM`) for
the new template. This option is required when using `source_iso`.
- `eject_iso` (boolean) - If `true` make a call to the CloudStack API, after
loading image to cache, requesting to check and detach ISO file (if any)
currently attached to a virtual machine. Defaults to `false`. This option
is only available when using `source_iso`.
- `eject_iso_delay` (time.Duration) - Configure the duration time to wait, making
sure virtual machine is able to finish installing OS before it ejects safely.
Requires `eject_iso` set to `true` and this option is only available when
using `source_iso`.
- `keypair` (string) - The name of the SSH key pair that will be used to
access the instance. The SSH key pair is assumed to be already available
within CloudStack.
- `instance_name` (string) - The name of the instance. Defaults to
"packer-UUID" where UUID is dynamically generated.
- `prevent_firewall_changes` (boolean) - Set to `true` to prevent network
ACLs or firewall rules creation. Defaults to `false`.
- `project` (string) - The name or ID of the project to deploy the instance
to.
- `public_ip_address` (string) - The public IP address or it's ID used for
connecting any provisioners to. If not provided, a temporary public IP
address will be associated and released during the Packer run.
- `public_port` (number) - The fixed port you want to configure in the port
forwarding rule. Set this attribute if you do not want to use the a random
public port.
- `security_groups` (array of strings) - A list of security group IDs or
names to associate the instance with.
- `ssl_no_verify` (boolean) - Set to `true` to skip SSL verification.
Defaults to `false`.
- `template_display_text` (string) - The display text of the new template.
Defaults to the `template_name`.
- `template_featured` (boolean) - Set to `true` to indicate that the template
is featured. Defaults to `false`.
- `template_name` (string) - The name of the new template. Defaults to
`packer-{{timestamp}}` where timestamp will be the current time.
- `template_public` (boolean) - Set to `true` to indicate that the template
is available for all accounts. Defaults to `false`.
- `template_password_enabled` (boolean) - Set to `true` to indicate the
template should be password enabled. Defaults to `false`.
- `template_requires_hvm` (boolean) - Set to `true` to indicate the template
requires hardware-assisted virtualization. Defaults to `false`.
- `template_scalable` (boolean) - Set to `true` to indicate that the template
contains tools to support dynamic scaling of VM cpu/memory. Defaults to
`false`.
- `temporary_keypair_name` (string) - The name of the temporary SSH key pair
to generate. By default, Packer generates a name that looks like
`packer_<UUID>`, where &lt;UUID&gt; is a 36 character unique identifier.
- `user_data` (string) - User data to launch with the instance. This is a
[template engine](/docs/templates/legacy_json_templates/engine) see _User
Data_ below for more details. Packer will not automatically wait for a user
script to finish before shutting down the instance this must be handled in a
provisioner.
- `user_data_file` (string) - Path to a file that will be used for the user
data when launching the instance. This file will be parsed as a [template
engine](/docs/templates/legacy_json_templates/engine) see _User Data_ below
for more details.
- `use_local_ip_address` (boolean) - Set to `true` to indicate that the
provisioners should connect to the local IP address of the instance.
## User Data
The available variables are:
- `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
that is started serving the directory specified by the `http_directory`
configuration parameter. If `http_directory` isn't specified, these will be
blank. Example: `{{.HTTPIP}}:{{.HTTPPort}}/path/to/a/file/in/http_directory`
### Communicator Configuration
#### Optional:
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSHTemporaryKeyPair-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Key-Pair-Name-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Agent-Auth-not-required.mdx'
## Http directory configuration
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig.mdx'
### Optional:
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig-not-required.mdx'
## Basic Example
Here is a basic example.
```json
{
"type": "cloudstack",
"api_url": "https://cloudstack.company.com/client/api",
"api_key": "YOUR_API_KEY",
"secret_key": "YOUR_SECRET_KEY",
"disk_offering": "Small - 20GB",
"hypervisor": "KVM",
"network": "management",
"service_offering": "small",
"source_iso": "CentOS-7.0-1406-x86_64-Minimal",
"zone": "NL1",
"ssh_username": "root",
"template_name": "Centos7-x86_64-KVM-Packer",
"template_display_text": "Centos7-x86_64 KVM Packer",
"template_featured": true,
"template_password_enabled": true,
"template_scalable": true,
"template_os": "Other PV (64-bit)"
}
```

View File

@ -1,186 +0,0 @@
---
description: |
The `tencentcloud-cvm` Packer builder plugin provide the capability to build
customized images based on an existing base images.
page_title: Tencentcloud Image Builder
---
# Tencentcloud Image Builder
Type: `tencentcloud-cvm`
Artifact BuilderId: `tencent.cloud`
The `tencentcloud-cvm` Packer builder plugin provide the capability to build
customized images based on an existing base images.
## Configuration Reference
The following configuration options are available for building Tencentcloud images.
In addition to the options listed here,
a [communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this builder.
### Required:
- `secret_id` (string) - Tencentcloud secret id. You should set it directly,
or set the `TENCENTCLOUD_ACCESS_KEY` environment variable.
- `secret_key` (string) - Tencentcloud secret key. You should set it directly,
or set the `TENCENTCLOUD_SECRET_KEY` environment variable.
- `region` (string) - The region 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) - 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.
- `instance_type` (string) - The instance type your cvm will be launched by.
You should reference [Instace Type](https://intl.cloud.tencent.com/document/product/213/11518)
for parameter taking.
- `source_image_id` (string) - The base image id of Image you want to create
your customized image from.
- `image_name` (string) - The name you want to create your customize image,
it should be composed of no more than 60 characters, of letters, numbers
or minus sign.
### Optional:
- `force_poweroff` (boolean) - Indicates whether to perform a forced shutdown to
create an image when soft shutdown fails. Default value is `false`.
- `image_description` (string) - Image description. It should no more than 60 characters.
- `reboot` (boolean, **deprecated**) - Whether shutdown cvm to create Image.
Please refer to parameter `force_poweroff`.
- `sysprep` (boolean) - Whether enable Sysprep during creating windows image.
- `image_copy_regions` (array of strings) - Regions that will be copied to after
your image created.
- `image_share_accounts` (array of strings) - Accounts that will be shared to
after your image created.
- `skip_region_validation` (boolean) - Do not check region and zone when validate.
- `associate_public_ip_address` (boolean) - Whether allocate public ip to your cvm.
Default value is `false`.
If not set, you could access your cvm from the same vpc.
- `internet_max_bandwidth_out` (number) - Max bandwidth out your cvm will be launched by(in MB).
values can be set between 1 ~ 100.
- `instance_name` (string) - Instance name.
- `disk_type` (string) - Root disk type your cvm will be launched by, default is `CLOUD_PREMIUM`. you could
reference [Disk Type](https://intl.cloud.tencent.com/document/product/213/15753#SystemDisk)
for parameter taking.
- `disk_size` (number) - Root disk size your cvm will be launched by. values range(in GB):
- LOCAL_BASIC: 50
- Other: 50 ~ 1000 (need whitelist if > 50)
- `data_disks` (array of data disks) - Add one or more data disks to the instance before creating the
image. Note that if the source image has data disk snapshots, this argument will be ignored, and
the running instance will use source image data disk settings, in such case, `disk_type`
argument will be used as disk 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_size` - Size of the data disk.
- `disk_snapshot_id` - Id of the snapshot for a data disk.
- `vpc_id` (string) - Specify vpc your cvm will be launched by.
- `vpc_name` (string) - Specify vpc name you will create. if `vpc_id` is not set, Packer will
create a vpc for you named this parameter.
- `cidr_block` (boolean) - Specify cider block of the vpc you will create if `vpc_id` is not set.
- `subnet_id` (string) - Specify subnet your cvm will be launched by.
- `subnet_name` (string) - Specify subnet name you will create. if `subnet_id` is not set, Packer will
create a subnet for you named this parameter.
- `subnect_cidr_block` (boolean) - Specify cider block of the subnet you will create if
`subnet_id` is not set.
- `security_group_id` (string) - Specify security group your cvm will be launched by.
- `security_group_name` (string) - Specify security name you will create if `security_group_id` is not set.
- `user_data` (string) - userdata.
- `user_data_file` (string) - userdata file.
- `host_name` (string) - host name.
- `run_tags` (map of strings) - Tags to apply to the instance that is _launched_ to create the image.
These tags are _not_ applied to the resulting image.
### Communicator Configuration
In addition to the above options, a communicator can be configured
for this builder.
#### Optional:
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSHTemporaryKeyPair-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Key-Pair-Name-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Agent-Auth-not-required.mdx'
## Basic Example
Here is a basic example for Tencentcloud.
```json
{
"variables": {
"secret_id": "{{env `TENCENTCLOUD_ACCESS_KEY`}}",
"secret_key": "{{env `TENCENTCLOUD_SECRET_KEY`}}"
},
"builders": [
{
"type": "tencentcloud-cvm",
"secret_id": "{{user `secret_id`}}",
"secret_key": "{{user `secret_key`}}",
"region": "ap-guangzhou",
"zone": "ap-guangzhou-4",
"instance_type": "S4.SMALL1",
"source_image_id": "img-oikl1tzv",
"ssh_username": "root",
"image_name": "PackerTest",
"disk_type": "CLOUD_PREMIUM",
"packer_debug": true,
"associate_public_ip_address": true,
"run_tags": {
"good": "luck"
}
}
],
"provisioners": [
{
"type": "shell",
"inline": ["sleep 30", "yum install redis.x86_64 -y"]
}
]
}
```
See the
[examples/tencentcloud](https://github.com/hashicorp/packer/tree/master/builder/tencentcloud/examples)
folder in the Packer project for more examples.

View File

@ -1,217 +0,0 @@
---
description: |
The Vagrant Packer builder is able to launch Vagrant boxes and
re-package them into .box files
page_title: Vagrant - Builders
---
# Vagrant Builder
Type: `vagrant`
Artifact BuilderId: `vagrant`
The Vagrant builder is intended for building new boxes from already-existing
boxes. Your source should be a URL or path to a .box file or a Vagrant Cloud
box name such as `hashicorp/precise64`.
Packer will not install vagrant, nor will it install the underlying
virtualization platforms or extra providers; We expect when you run this
builder that you have already installed what you need.
By default, this builder will initialize a new Vagrant workspace, launch your
box from that workspace, provision it, call `vagrant package` to package it
into a new box, and then destroy the original box. Please note that vagrant
will _not_ remove the box file from your system (we don't call
`vagrant box remove`).
You can change the behavior so that the builder doesn't destroy the box by
setting the `teardown_method` option. You can change the behavior so the builder
doesn't package it (not all provisioners support the `vagrant package` command)
by setting the `skip package` option. You can also change the behavior so that
rather than initializing a new Vagrant workspace, you use an already defined
one, by using `global_id` instead of `source_box`.
Please note that if you are using the Vagrant builder, then the Vagrant
post-processor is unnecesary because the output of the Vagrant builder is
already a Vagrant box; using that post-processor with the Vagrant builder will
cause your build to fail. Similarly, since Vagrant boxes are already compressed,
the Compress post-processor will not work with this builder.
## Configuration Reference
### Required:
- `source_path` (string) - URL of the vagrant box to use, or the name of the
vagrant box. `hashicorp/precise64`, `./mylocalbox.box` and
`https://example.com/my-box.box` are all valid source boxes. If your
source is a .box file, whether locally or from a URL like the latter example
above, you will also need to provide a `box_name`. This option is required,
unless you set `global_id`. You may only set one or the other, not both.
or
- `global_id` (string) - the global id of a Vagrant box already added to Vagrant
on your system. You can find the global id of your Vagrant boxes using the
command `vagrant global-status`; your global_id will be a 7-digit number and
letter combination that you'll find in the leftmost column of the
global-status output. If you choose to use `global_id` instead of
`source_box`, Packer will skip the Vagrant initialize and add steps, and
simply launch the box directly using the global id.
### Optional:
- `output_dir` (string) - The directory to create that will contain
your output box. We always create this directory and run from inside of it to
prevent Vagrant init collisions. If unset, it will be set to `output-` plus
your buildname.
- `box_name` (string) - if your source_box is a boxfile that we need to add
to Vagrant, this is the name to give it. If left blank, will default to
"packer\_" plus your buildname.
- `provider` (string) - The vagrant [provider](/docs/post-processors/vagrant).
This parameter is required when `source_path` have more than one provider,
or when using `vagrant-cloud` post-processor. Defaults to unset.
- `checksum` (string) - The checksum for the .box file. The type of the
checksum is specified with `checksum_type`, documented below.
- `checksum_type` (string) - The type of the checksum specified in `checksum`.
Valid values are `none`, `md5`, `sha1`, `sha256`, or `sha512`. Although the
checksum will not be verified when `checksum_type` is set to "none", this is
not recommended since OVA files can be very large and corruption does happen
from time to time.
- `template` (string) - a path to a golang template for a
vagrantfile. Our default template can be found
[here](https://github.com/hashicorp/packer/blob/master/builder/vagrant/step_create_vagrantfile.go#L23-L37). So far the only template variables available to you are `{{ .BoxName }}` and
`{{ .SyncedFolder }}`, which correspond to the Packer options `box_name` and
`synced_folder`.
You must provide a template if your default vagrant provider is Hyper-V.
Below is a Hyper-V compatible template.
```ruby
Vagrant.configure("2") do |config|
config.vm.box = "{{ .BoxName }}"
config.vm.network 'public_network', bridge: 'Default Switch'
end
```
- `skip_add` (bool) - Don't call "vagrant add" to add the box to your local
environment; this is necessary if you want to launch a box that is already
added to your vagrant environment.
- `teardown_method` (string) - Whether to halt, suspend, or destroy the box when
the build has completed. Defaults to "halt"
- `box_version` (string) - What box version to use when initializing Vagrant.
- `add_cacert` (string) - Equivalent to setting the
[`--cacert`](https://www.vagrantup.com/docs/cli/box.html#cacert-certfile)
option in `vagrant add`; defaults to unset.
- `add_capath` (string) - Equivalent to setting the
[`--capath`](https://www.vagrantup.com/docs/cli/box.html#capath-certdir) option
in `vagrant add`; defaults to unset.
- `add_cert` (string) - Equivalent to setting the
[`--cert`](https://www.vagrantup.com/docs/cli/box.html#cert-certfile) option in
`vagrant add`; defaults to unset.
- `add_clean` (bool) - Equivalent to setting the
[`--clean`](https://www.vagrantup.com/docs/cli/box.html#clean) flag in
`vagrant add`; defaults to unset.
- `add_force` (bool) - Equivalent to setting the
[`--force`](https://www.vagrantup.com/docs/cli/box.html#force) flag in
`vagrant add`; defaults to unset.
- `add_insecure` (bool) - Equivalent to setting the
[`--insecure`](https://www.vagrantup.com/docs/cli/box.html#insecure) flag in
`vagrant add`; defaults to unset.
- `skip_package` (bool) - if true, Packer will not call `vagrant package` to
package your base box into its own standalone .box file.
- `output_vagrantfile` (string) - Equivalent to setting the
[`--vagrantfile`](https://www.vagrantup.com/docs/cli/package.html#vagrantfile-file) option
in `vagrant package`; defaults to unset
- `package_include` (string) - Equivalent to setting the
[`--include`](https://www.vagrantup.com/docs/cli/package.html#include-x-y-z) option
in `vagrant package`; defaults to unset
## Example
Sample for `hashicorp/precise64` with virtualbox provider.
<Tabs>
<Tab heading="JSON">
```json
{
"builders": [
{
"communicator": "ssh",
"source_path": "hashicorp/precise64",
"provider": "virtualbox",
"add_force": true,
"type": "vagrant"
}
]
}
```
</Tab>
<Tab heading="HCL2">
```hcl
source "vagrant" "example" {
communicator = "ssh"
source_path = "hashicorp/precise64"
provider = "virtualbox"
add_force = true
}
build {
sources = ["source.vagrant.example"]
}
```
</Tab>
</Tabs>
## Regarding output directory and new box
After Packer completes building and provisioning a new Vagrant Box file, it is worth
noting that the new box file will need to be added to Vagrant. For a beginner to Packer
and Vagrant, it may seem as if a simple 'vagrant up' in the output directory will run the
the newly created Box. This is not the case.
Rather, create a new directory (to avoid Vagarant init collisions), add the new
package.box to Vagrant and init. Then run vagrant up to bring up the new box created
by Packer. You will now be able to connect to the new box with provisioned changes.
```
'mkdir output2'
'cp package.box ./output2'
'vagrant box add new-box name-of-the-packer-box.box'
'vagrant init new-box'
'vagrant up'
```
## A note on SSH connections
Currently this builder only works for SSH connections, and automatically fills
in all information needed for the SSH communicator using vagrant's ssh-config.
If you would like to connect via a different username or authentication method
than is produced when you call `vagrant ssh-config`, then you must provide the
`ssh_username` and all other relevant authentication information (e.g.
`ssh_password` or `ssh_private_key_file`)
By providing the `ssh_username`, you're telling Packer not to use the vagrant
ssh config, except for determining the host and port for the virtual machine to
connect to.

View File

@ -1,5 +0,0 @@
<!-- Code generated from the comments of the TencentCloudAccessConfig struct in builder/tencentcloud/cvm/access_config.go; DO NOT EDIT MANUALLY -->
- `skip_region_validation` (bool) - Do not check region and zone when validate.
<!-- End of code generated from the comments of the TencentCloudAccessConfig struct in builder/tencentcloud/cvm/access_config.go; -->

View File

@ -1,17 +0,0 @@
<!-- Code generated from the comments of the TencentCloudAccessConfig struct in builder/tencentcloud/cvm/access_config.go; DO NOT EDIT MANUALLY -->
- `secret_id` (string) - Tencentcloud secret id. You should set it directly,
or set the TENCENTCLOUD_ACCESS_KEY environment variable.
- `secret_key` (string) - Tencentcloud secret key. You should set it directly,
or set the TENCENTCLOUD_SECRET_KEY environment variable.
- `region` (string) - The region where your cvm will be launch. You should
reference Region and Zone
for parameter taking.
- `zone` (string) - The zone where your cvm will be launch. You should
reference Region and Zone
for parameter taking.
<!-- End of code generated from the comments of the TencentCloudAccessConfig struct in builder/tencentcloud/cvm/access_config.go; -->

View File

@ -1,23 +0,0 @@
<!-- Code generated from the comments of the TencentCloudImageConfig struct in builder/tencentcloud/cvm/image_config.go; DO NOT EDIT MANUALLY -->
- `image_description` (string) - Image description.
- `reboot` (bool) - Whether shutdown cvm to create Image. Default value is
false.
- `force_poweroff` (bool) - Whether to force power off cvm when create image.
Default value is false.
- `sysprep` (bool) - Whether enable Sysprep during creating windows image.
- `image_force_delete` (bool) - Image Force Delete
- `image_copy_regions` ([]string) - regions that will be copied to after
your image created.
- `image_share_accounts` ([]string) - accounts that will be shared to
after your image created.
- `skip_region_validation` (bool) - Do not check region and zone when validate.
<!-- End of code generated from the comments of the TencentCloudImageConfig struct in builder/tencentcloud/cvm/image_config.go; -->

View File

@ -1,7 +0,0 @@
<!-- Code generated from the comments of the TencentCloudImageConfig struct in builder/tencentcloud/cvm/image_config.go; DO NOT EDIT MANUALLY -->
- `image_name` (string) - The name you want to create your customize image,
it should be composed of no more than 60 characters, of letters, numbers
or minus sign.
<!-- End of code generated from the comments of the TencentCloudImageConfig struct in builder/tencentcloud/cvm/image_config.go; -->

View File

@ -1,73 +0,0 @@
<!-- Code generated from the comments of the TencentCloudRunConfig struct in builder/tencentcloud/cvm/run_config.go; DO NOT EDIT MANUALLY -->
- `associate_public_ip_address` (bool) - Whether allocate public ip to your cvm.
Default value is false.
- `source_image_id` (string) - The base image id of Image you want to create
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.
- `instance_name` (string) - Instance name.
- `disk_type` (string) - Root disk type your cvm will be launched by, default is `CLOUD_PREMIUM`. you could
reference Disk Type
for parameter taking.
- `disk_size` (int64) - Root disk size your cvm will be launched by. values range(in GB):
- `data_disks` ([]tencentCloudDataDisk) - Add one or more data disks to the instance before creating the image.
Note that if the source image has data disk snapshots, this argument
will be ignored, and the running instance will use source image data
disk settings, in such case, `disk_type` argument will be used as disk
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_size` - Size of the data disk.
- `disk_snapshot_id` - Id of the snapshot for a data disk.
- `vpc_id` (string) - Specify vpc your cvm will be launched by.
- `vpc_name` (string) - Specify vpc name you will create. if vpc_id is not set, packer will
create a vpc for you named this parameter.
- `vpc_ip` (string) - Vpc Ip
- `subnet_id` (string) - Specify subnet your cvm will be launched by.
- `subnet_name` (string) - Specify subnet name you will create. if subnet_id is not set, packer will
create a subnet for you named this parameter.
- `cidr_block` (string) - Specify cider block of the vpc you will create if vpc_id not set
- `subnect_cidr_block` (string) - Specify cider block of the subnet you will create if
subnet_id not set
- `internet_charge_type` (string) - Internet Charge Type
- `internet_max_bandwidth_out` (int64) - Max bandwidth out your cvm will be launched by(in MB).
values can be set between 1 ~ 100.
- `security_group_id` (string) - Specify securitygroup your cvm will be launched by.
- `security_group_name` (string) - Specify security name you will create if security_group_id not set.
- `user_data` (string) - userdata.
- `user_data_file` (string) - userdata file.
- `host_name` (string) - host name.
- `run_tags` (map[string]string) - Key/value pair tags to apply to the instance that is *launched* to
create the image. These tags are *not* applied to the resulting image.
- `run_tag` ([]{key string, value string}) - Same as [`run_tags`](#run_tags) but defined as a singular repeatable
block containing a `key` and a `value` field. In HCL2 mode the
[`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
will allow you to create those programatically.
- `ssh_private_ip` (bool) - SSH Private Ip
<!-- End of code generated from the comments of the TencentCloudRunConfig struct in builder/tencentcloud/cvm/run_config.go; -->

View File

@ -1,7 +0,0 @@
<!-- Code generated from the comments of the TencentCloudRunConfig struct in builder/tencentcloud/cvm/run_config.go; DO NOT EDIT MANUALLY -->
- `instance_type` (string) - The instance type your cvm will be launched by.
You should reference Instace Type
for parameter taking.
<!-- End of code generated from the comments of the TencentCloudRunConfig struct in builder/tencentcloud/cvm/run_config.go; -->

View File

@ -1,9 +0,0 @@
<!-- Code generated from the comments of the tencentCloudDataDisk struct in builder/tencentcloud/cvm/run_config.go; DO NOT EDIT MANUALLY -->
- `disk_type` (string) - Disk Type
- `disk_size` (int64) - Disk Size
- `disk_snapshot_id` (string) - Snapshot Id
<!-- End of code generated from the comments of the tencentCloudDataDisk struct in builder/tencentcloud/cvm/run_config.go; -->

View File

@ -712,10 +712,6 @@
"title": "ProfitBricks",
"path": "builders/profitbricks"
},
{
"title": "Tencent Cloud",
"path": "builders/tencentcloud-cvm"
},
{
"title": "Yandex.Cloud",
"path": "builders/yandex"

View File

@ -167,6 +167,12 @@
"pluginTier": "community",
"version": "latest"
},
{
"title": "Tencent Cloud",
"path": "tencentcloud",
"repo": "hashicorp/packer-plugin-tencentcloud",
"version": "latest"
},
{
"title": "Triton",
"path": "triton",