Extract Tencent Cloud (#10967)
* extract and vendor tencentcloud plugin * fix fmt
This commit is contained in:
parent
ef612c0eb1
commit
bcb25f1916
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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) {}
|
|
@ -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) {}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
|
@ -47,15 +46,14 @@ type PluginCommand struct {
|
|||
}
|
||||
|
||||
var Builders = map[string]packersdk.Builder{
|
||||
"azure-arm": new(azurearmbuilder.Builder),
|
||||
"azure-chroot": new(azurechrootbuilder.Builder),
|
||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||
"file": new(filebuilder.Builder),
|
||||
"null": new(nullbuilder.Builder),
|
||||
"oneandone": new(oneandonebuilder.Builder),
|
||||
"profitbricks": new(profitbricksbuilder.Builder),
|
||||
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
|
||||
"yandex": new(yandexbuilder.Builder),
|
||||
"azure-arm": new(azurearmbuilder.Builder),
|
||||
"azure-chroot": new(azurechrootbuilder.Builder),
|
||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||
"file": new(filebuilder.Builder),
|
||||
"null": new(nullbuilder.Builder),
|
||||
"oneandone": new(oneandonebuilder.Builder),
|
||||
"profitbricks": new(profitbricksbuilder.Builder),
|
||||
"yandex": new(yandexbuilder.Builder),
|
||||
}
|
||||
|
||||
var Provisioners = map[string]packersdk.Provisioner{
|
||||
|
|
|
@ -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
3
go.mod
|
@ -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
3
go.sum
|
@ -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=
|
||||
|
|
|
@ -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 <UUID> 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)"
|
||||
}
|
||||
```
|
|
@ -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.
|
|
@ -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.
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -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; -->
|
|
@ -712,10 +712,6 @@
|
|||
"title": "ProfitBricks",
|
||||
"path": "builders/profitbricks"
|
||||
},
|
||||
{
|
||||
"title": "Tencent Cloud",
|
||||
"path": "builders/tencentcloud-cvm"
|
||||
},
|
||||
{
|
||||
"title": "Yandex.Cloud",
|
||||
"path": "builders/yandex"
|
||||
|
|
|
@ -167,6 +167,12 @@
|
|||
"pluginTier": "community",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Tencent Cloud",
|
||||
"path": "tencentcloud",
|
||||
"repo": "hashicorp/packer-plugin-tencentcloud",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Triton",
|
||||
"path": "triton",
|
||||
|
|
Loading…
Reference in New Issue