diff --git a/builder/tencentcloud/cvm/access_config.go b/builder/tencentcloud/cvm/access_config.go
index cb4790131..9dbe482e4 100644
--- a/builder/tencentcloud/cvm/access_config.go
+++ b/builder/tencentcloud/cvm/access_config.go
@@ -3,6 +3,7 @@
package cvm
import (
+ "context"
"fmt"
"os"
@@ -71,49 +72,67 @@ func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) {
vpc_client *vpc.Client
resp *cvm.DescribeZonesResponse
)
+
if err = cf.validateRegion(); err != nil {
return nil, nil, err
}
- credential := common.NewCredential(
- cf.SecretId, cf.SecretKey)
+
+ if cf.Zone == "" {
+ return nil, nil, fmt.Errorf("parameter zone must be set")
+ }
+
+ credential := common.NewCredential(cf.SecretId, cf.SecretKey)
cpf := profile.NewClientProfile()
+ cpf.HttpProfile.ReqMethod = "POST"
+ cpf.HttpProfile.ReqTimeout = 300
+ cpf.Language = "en-US"
+
if cvm_client, err = cvm.NewClient(credential, cf.Region, cpf); err != nil {
return nil, nil, err
}
+
if vpc_client, err = vpc.NewClient(credential, cf.Region, cpf); err != nil {
return nil, nil, err
}
- if resp, err = cvm_client.DescribeZones(nil); err != nil {
+
+ 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
}
- if cf.Zone != "" {
- for _, zone := range resp.Response.ZoneSet {
- if cf.Zone == *zone.Zone {
- return cvm_client, vpc_client, nil
- }
+
+ 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)
- } else {
- return nil, nil, fmt.Errorf("zone must be set")
}
+
+ 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("region must be set"))
+ 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
}
@@ -121,20 +140,28 @@ 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("TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY must be set")
+ 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 valid == Region(cf.Region) {
+ if Region(region) == valid {
return nil
}
}
- return fmt.Errorf("unknown region: %s", cf.Region)
+
+ return fmt.Errorf("unknown region: %s", region)
}
diff --git a/builder/tencentcloud/cvm/artifact.go b/builder/tencentcloud/cvm/artifact.go
index 964f8c8a7..b5c38f579 100644
--- a/builder/tencentcloud/cvm/artifact.go
+++ b/builder/tencentcloud/cvm/artifact.go
@@ -1,6 +1,7 @@
package cvm
import (
+ "context"
"fmt"
"log"
"sort"
@@ -29,8 +30,8 @@ func (a *Artifact) Id() string {
for region, imageId := range a.TencentCloudImages {
parts = append(parts, fmt.Sprintf("%s:%s", region, imageId))
}
-
sort.Strings(parts)
+
return strings.Join(parts, ",")
}
@@ -40,7 +41,8 @@ func (a *Artifact) String() string {
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"))
+
+ return fmt.Sprintf("Tencentcloud images(%s) were created.\n\n", strings.Join(parts, "\n"))
}
func (a *Artifact) State(name string) interface{} {
@@ -53,6 +55,7 @@ func (a *Artifact) State(name string) interface{} {
}
func (a *Artifact) Destroy() error {
+ ctx := context.TODO()
errors := make([]error, 0)
for region, imageId := range a.TencentCloudImages {
@@ -60,22 +63,31 @@ func (a *Artifact) Destroy() error {
describeReq := cvm.NewDescribeImagesRequest()
describeReq.ImageIds = []*string{&imageId}
-
- describeResp, err := a.Client.DescribeImages(describeReq)
+ 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
-
- describeShareResp, err := a.Client.DescribeImageSharePermission(describeShareReq)
- var shareAccountIds []*string = nil
+ 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 {
@@ -90,7 +102,10 @@ func (a *Artifact) Destroy() error {
cancelShareReq.AccountIds = shareAccountIds
CANCEL := "CANCEL"
cancelShareReq.Permission = &CANCEL
- _, err := a.Client.ModifyImageSharePermission(cancelShareReq)
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := a.Client.ModifyImageSharePermission(cancelShareReq)
+ return e
+ })
if err != nil {
errors = append(errors, err)
}
@@ -98,8 +113,10 @@ func (a *Artifact) Destroy() error {
deleteReq := cvm.NewDeleteImagesRequest()
deleteReq.ImageIds = []*string{&imageId}
-
- _, err = a.Client.DeleteImages(deleteReq)
+ err = Retry(ctx, func(ctx context.Context) error {
+ _, e := a.Client.DeleteImages(deleteReq)
+ return e
+ })
if err != nil {
errors = append(errors, err)
}
@@ -120,5 +137,6 @@ func (a *Artifact) stateAtlasMetadata() interface{} {
k := fmt.Sprintf("region.%s", region)
metadata[k] = imageId
}
+
return metadata
}
diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go
index 9b05846b3..9fe8abb74 100644
--- a/builder/tencentcloud/cvm/builder.go
+++ b/builder/tencentcloud/cvm/builder.go
@@ -49,13 +49,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, b.config.TencentCloudAccessConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.TencentCloudImageConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.TencentCloudRunConfig.Prepare(&b.config.ctx)...)
-
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packer.LogSecretFilter.Set(b.config.SecretId, b.config.SecretKey)
- log.Println(b.config)
+ log.Printf("[DEBUG]packer config: %v", b.config)
+
return nil, nil
}
@@ -64,17 +64,21 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
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)
- var steps []multistep.Step
// Build the steps
+ var steps []multistep.Step
steps = []multistep.Step{
- &stepCheckSourceImage{b.config.SourceImageId},
+ &stepPreValidate{},
+ &stepCheckSourceImage{
+ b.config.SourceImageId,
+ },
&stepConfigKeyPair{
Debug: b.config.PackerDebug,
Comm: &b.config.Comm,
@@ -94,7 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&stepConfigSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
SecurityGroupName: b.config.SecurityGroupName,
- Description: "a simple security group",
+ Description: "securitygroup for packer",
},
&stepRunInstance{
InstanceType: b.config.InstanceType,
@@ -119,7 +123,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&common.StepCleanupTempKeys{
Comm: &b.config.TencentCloudRunConfig.Comm,
},
- // We need this step to detach temporary key from instance, otherwise
+ // We need this step to detach keypair from instance, otherwise
// it always fails to delete the key.
&stepDetachTempKeyPair{},
&stepCreateImage{},
@@ -148,5 +152,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
BuilderIdValue: BuilderId,
Client: cvmClient,
}
+
return artifact, nil
}
diff --git a/builder/tencentcloud/cvm/common.go b/builder/tencentcloud/cvm/common.go
index 209ae6319..6b2f3b374 100644
--- a/builder/tencentcloud/cvm/common.go
+++ b/builder/tencentcloud/cvm/common.go
@@ -1,44 +1,35 @@
package cvm
import (
+ "context"
"fmt"
"regexp"
+ "strings"
"time"
+ "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
-func CheckResourceIdFormat(resource string, id string) bool {
- regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource))
- if !regex.MatchString(id) {
- return false
- }
- return true
-}
-
-func MessageClean(state multistep.StateBag, module string) {
- _, cancelled := state.GetOk(multistep.StateCancelled)
- _, halted := state.GetOk(multistep.StateHalted)
-
- ui := state.Get("ui").(packer.Ui)
-
- if cancelled || halted {
- ui.Say(fmt.Sprintf("Deleting %s because of cancellation or error...", module))
- } else {
- ui.Say(fmt.Sprintf("Cleaning up '%s'", module))
- }
-
-}
-
+// DefaultWaitForInterval is sleep interval when wait statue
const DefaultWaitForInterval = 5
+// WaitForInstance wait for instance reaches statue
func WaitForInstance(client *cvm.Client, instanceId string, status string, timeout int) error {
+ ctx := context.TODO()
req := cvm.NewDescribeInstancesRequest()
req.InstanceIds = []*string{&instanceId}
+
for {
- resp, err := client.DescribeInstances(req)
+ 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
}
@@ -54,10 +45,13 @@ func WaitForInstance(client *cvm.Client, instanceId string, status string, timeo
return fmt.Errorf("wait instance(%s) status(%s) timeout", instanceId, status)
}
}
+
return nil
}
+// WaitForImageReady wait for image reaches statue
func WaitForImageReady(client *cvm.Client, imageName string, status string, timeout int) error {
+ ctx := context.TODO()
req := cvm.NewDescribeImagesRequest()
FILTER_IMAGE_NAME := "image-name"
req.Filters = []*cvm.Filter{
@@ -66,8 +60,14 @@ func WaitForImageReady(client *cvm.Client, imageName string, status string, time
Values: []*string{&imageName},
},
}
+
for {
- resp, err := client.DescribeImages(req)
+ 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 err
}
@@ -84,12 +84,22 @@ func WaitForImageReady(client *cvm.Client, imageName string, status string, time
time.Sleep(DefaultWaitForInterval * time.Second)
timeout = timeout - DefaultWaitForInterval
if timeout <= 0 {
- return fmt.Errorf("wait image(%s) ready timeout", imageName)
+ return fmt.Errorf("wait image(%s) status(%s) timeout", imageName, status)
}
}
+
return nil
}
+// CheckResourceIdFormat check resource id format
+func CheckResourceIdFormat(resource string, id string) bool {
+ regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource))
+ if !regex.MatchString(id) {
+ return false
+ }
+ return true
+}
+
// 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) {
@@ -101,3 +111,83 @@ func SSHHost(pubilcIp bool) func(multistep.StateBag) (string, error) {
}
}
}
+
+// Retry do retry on api request
+func Retry(ctx context.Context, fn func(context.Context) error) error {
+ return retry.Config{
+ Tries: 30,
+ 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 == "InvalidInstance.NotSupported" ||
+ 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").(packer.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").(packer.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").(packer.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
+}
diff --git a/builder/tencentcloud/cvm/image_config.go b/builder/tencentcloud/cvm/image_config.go
index 5311f4254..353675d0f 100644
--- a/builder/tencentcloud/cvm/image_config.go
+++ b/builder/tencentcloud/cvm/image_config.go
@@ -36,6 +36,7 @@ type TencentCloudImageConfig struct {
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"))
@@ -68,17 +69,10 @@ func (cf *TencentCloudImageConfig) Prepare(ctx *interpolate.Context) []error {
}
cf.ImageCopyRegions = regions
}
+
if len(errs) > 0 {
return errs
}
- return nil
-}
-func validRegion(region string) error {
- for _, valid := range ValidRegions {
- if Region(region) == valid {
- return nil
- }
- }
- return fmt.Errorf("unknown region: %s", region)
+ return nil
}
diff --git a/builder/tencentcloud/cvm/image_config_test.go b/builder/tencentcloud/cvm/image_config_test.go
index 53f9240b7..505dcf3a5 100644
--- a/builder/tencentcloud/cvm/image_config_test.go
+++ b/builder/tencentcloud/cvm/image_config_test.go
@@ -31,5 +31,4 @@ func TestTencentCloudImageConfig_Prepare(t *testing.T) {
if err := cf.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err:%v", err)
}
-
}
diff --git a/builder/tencentcloud/cvm/run_config.go b/builder/tencentcloud/cvm/run_config.go
index 2e4bc3cfe..848566bde 100644
--- a/builder/tencentcloud/cvm/run_config.go
+++ b/builder/tencentcloud/cvm/run_config.go
@@ -5,6 +5,7 @@ package cvm
import (
"fmt"
"os"
+ "strings"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
@@ -68,7 +69,7 @@ type TencentCloudRunConfig struct {
// 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 security group your cvm will be launched by.
+ // 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"`
@@ -92,10 +93,11 @@ var ValidCBSType = []string{
}
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 = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()[:8])
+ cf.Comm.SSHTemporaryKeyPairName = packerId
}
errs := cf.Comm.Prepare(ctx)
@@ -126,7 +128,7 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error {
if cf.VpcId == "" {
if cf.VpcName == "" {
- cf.VpcName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
+ cf.VpcName = packerId
}
if cf.CidrBlock == "" {
cf.CidrBlock = "10.0.0.0/16"
@@ -135,9 +137,10 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, errors.New("can't set subnet_id without set vpc_id"))
}
}
+
if cf.SubnetId == "" {
if cf.SubnetName == "" {
- cf.SubnetName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
+ cf.SubnetName = packerId
}
if cf.SubnectCidrBlock == "" {
cf.SubnectCidrBlock = "10.0.8.0/24"
@@ -145,7 +148,7 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error {
}
if cf.SecurityGroupId == "" && cf.SecurityGroupName == "" {
- cf.SecurityGroupName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
+ cf.SecurityGroupName = packerId
}
if cf.DiskType != "" && !checkDiskType(cf.DiskType) {
@@ -163,12 +166,17 @@ func (cf *TencentCloudRunConfig) Prepare(ctx *interpolate.Context) []error {
}
if cf.InstanceName == "" {
- cf.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
+ cf.InstanceName = packerId
}
if cf.HostName == "" {
- cf.HostName = cf.InstanceName[:15]
+ 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)
@@ -183,5 +191,6 @@ func checkDiskType(diskType string) bool {
return true
}
}
+
return false
}
diff --git a/builder/tencentcloud/cvm/run_config_test.go b/builder/tencentcloud/cvm/run_config_test.go
index d6ffb889b..8f62c9f90 100644
--- a/builder/tencentcloud/cvm/run_config_test.go
+++ b/builder/tencentcloud/cvm/run_config_test.go
@@ -60,6 +60,7 @@ func TestTencentCloudRunConfig_Prepare(t *testing.T) {
func TestTencentCloudRunConfigPrepare_UserData(t *testing.T) {
cf := testConfig()
+
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("new temp file failed: %v", err)
@@ -76,6 +77,7 @@ func TestTencentCloudRunConfigPrepare_UserData(t *testing.T) {
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")
@@ -117,13 +119,16 @@ func TestTencentCloudRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
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)
}
diff --git a/builder/tencentcloud/cvm/step_check_source_image.go b/builder/tencentcloud/cvm/step_check_source_image.go
index 342373885..f9af0bf24 100644
--- a/builder/tencentcloud/cvm/step_check_source_image.go
+++ b/builder/tencentcloud/cvm/step_check_source_image.go
@@ -5,7 +5,6 @@ import (
"fmt"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -14,32 +13,31 @@ type stepCheckSourceImage struct {
}
func (s *stepCheckSourceImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
- client := state.Get("cvm_client").(*cvm.Client)
config := state.Get("config").(*Config)
- ui := state.Get("ui").(packer.Ui)
+ client := state.Get("cvm_client").(*cvm.Client)
+
+ Say(state, config.SourceImageId, "Trying to check source image")
req := cvm.NewDescribeImagesRequest()
req.ImageIds = []*string{&config.SourceImageId}
req.InstanceType = &config.InstanceType
-
- resp, err := client.DescribeImages(req)
+ 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 {
- err := fmt.Errorf("querying image info failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to get source image info")
}
- if *resp.Response.TotalCount > 0 { // public image or private image.
+ if *resp.Response.TotalCount > 0 {
state.Put("source_image", resp.Response.ImageSet[0])
- ui.Message(fmt.Sprintf("Image found: %s", *resp.Response.ImageSet[0].ImageId))
+ Message(state, *resp.Response.ImageSet[0].ImageName, "Image found")
return multistep.ActionContinue
}
- // later market image will be included.
- err = fmt.Errorf("no image founded under current instance_type(%s) restriction", config.InstanceType)
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+
+ return Halt(state, fmt.Errorf("No image found under current instance_type(%s) restriction", config.InstanceType), "")
}
func (s *stepCheckSourceImage) Cleanup(bag multistep.StateBag) {}
diff --git a/builder/tencentcloud/cvm/step_config_key_pair.go b/builder/tencentcloud/cvm/step_config_key_pair.go
index dfaa97a0d..a4ae97159 100644
--- a/builder/tencentcloud/cvm/step_config_key_pair.go
+++ b/builder/tencentcloud/cvm/step_config_key_pair.go
@@ -6,12 +6,9 @@ import (
"io/ioutil"
"os"
"runtime"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -19,83 +16,79 @@ type stepConfigKeyPair struct {
Debug bool
Comm *communicator.Config
DebugKeyPath string
-
- keyID string
+ keyID string
}
func (s *stepConfigKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
- ui := state.Get("ui").(packer.Ui)
+ client := state.Get("cvm_client").(*cvm.Client)
if s.Comm.SSHPrivateKeyFile != "" {
- ui.Say("Using existing SSH private key")
+ Say(state, "Using existing SSH private key", "")
privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
if err != nil {
- state.Put("error", fmt.Errorf(
- "loading configured private key file failed: %s", err))
- return multistep.ActionHalt
+ 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 && s.Comm.SSHKeyPairName == "" {
- ui.Say("Using SSH Agent with key pair in source image")
- return multistep.ActionContinue
- }
-
- if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
- ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
+ 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 == "" {
- ui.Say("Not using temporary keypair")
+ Say(state, "Not to use temporary keypair", "")
s.Comm.SSHKeyPairName = ""
return multistep.ActionContinue
}
- client := state.Get("cvm_client").(*cvm.Client)
- ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
+ 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
- resp, err := client.CreateKeyPair(req)
+ 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 {
- state.Put("error", fmt.Errorf("creating temporary keypair failed: %s", err.Error()))
- return multistep.ActionHalt
+ 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", 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 {
- ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
+ Message(state, fmt.Sprintf("Saving temporary key to %s for debug purposes", s.DebugKeyPath), "")
f, err := os.Create(s.DebugKeyPath)
if err != nil {
- state.Put("error", fmt.Errorf("creating debug key file failed:%s", err.Error()))
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to saving debug key file")
}
defer f.Close()
-
if _, err := f.Write([]byte(*resp.Response.KeyPair.PrivateKey)); err != nil {
- state.Put("error", fmt.Errorf("writing debug key file failed:%s", err.Error()))
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to writing debug key file")
}
-
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
- state.Put("error", fmt.Errorf("setting debug key file's permission failed:%s", err.Error()))
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to chmod debug key file")
}
}
}
+
return multistep.ActionContinue
}
@@ -103,28 +96,25 @@ func (s *stepConfigKeyPair) Cleanup(state multistep.StateBag) {
if s.Comm.SSHPrivateKeyFile != "" || (s.Comm.SSHKeyPairName == "" && s.keyID == "") {
return
}
- ctx := context.TODO()
+ ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
- ui := state.Get("ui").(packer.Ui)
- ui.Say("Deleting temporary keypair...")
+ SayClean(state, "keypair")
+
req := cvm.NewDeleteKeyPairsRequest()
req.KeyIds = []*string{&s.keyID}
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 5 * time.Second, Multiplier: 2}).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := client.DeleteKeyPairs(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.DeleteKeyPairs(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf(
- "delete keypair failed, please delete it manually, keyId: %s, err: %s", s.keyID, err.Error()))
+ 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 {
- ui.Error(fmt.Sprintf("delete debug key file %s failed: %s", s.DebugKeyPath, err.Error()))
+ Error(state, err, fmt.Sprintf("Failed to delete debug key file(%s), please delete it manually", s.DebugKeyPath))
}
}
}
diff --git a/builder/tencentcloud/cvm/step_config_security_group.go b/builder/tencentcloud/cvm/step_config_security_group.go
index 2b107615a..97d70e3c1 100644
--- a/builder/tencentcloud/cvm/step_config_security_group.go
+++ b/builder/tencentcloud/cvm/step_config_security_group.go
@@ -2,14 +2,9 @@ package cvm
import (
"context"
- "time"
-
"fmt"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
- "github.com/pkg/errors"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
@@ -22,42 +17,51 @@ type stepConfigSecurityGroup struct {
func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
- if len(s.SecurityGroupId) != 0 { // use existing security group
+ if len(s.SecurityGroupId) != 0 {
+ Say(state, s.SecurityGroupId, "Trying to use existing securitygroup")
req := vpc.NewDescribeSecurityGroupsRequest()
req.SecurityGroupIds = []*string{&s.SecurityGroupId}
- resp, err := vpcClient.DescribeSecurityGroups(req)
+ 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 {
- ui.Error(fmt.Sprintf("query security group failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to get securitygroup info")
}
if *resp.Response.TotalCount > 0 {
- state.Put("security_group_id", s.SecurityGroupId)
s.isCreate = false
+ state.Put("security_group_id", s.SecurityGroupId)
+ Message(state, *resp.Response.SecurityGroupSet[0].SecurityGroupName, "Securitygroup found")
return multistep.ActionContinue
}
- message := fmt.Sprintf("the specified security group(%s) does not exist", s.SecurityGroupId)
- ui.Error(message)
- state.Put("error", errors.New(message))
- return multistep.ActionHalt
+ return Halt(state, fmt.Errorf("The specified securitygroup(%s) does not exists", s.SecurityGroupId), "")
}
- // create a new security group
+
+ Say(state, "Trying to create a new securitygroup", "")
+
req := vpc.NewCreateSecurityGroupRequest()
req.GroupName = &s.SecurityGroupName
req.GroupDescription = &s.Description
- resp, err := vpcClient.CreateSecurityGroup(req)
+ 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 {
- ui.Error(fmt.Sprintf("create security group failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to create securitygroup")
}
+
+ s.isCreate = true
s.SecurityGroupId = *resp.Response.SecurityGroup.SecurityGroupId
state.Put("security_group_id", s.SecurityGroupId)
- s.isCreate = true
+ Message(state, s.SecurityGroupId, "Securitygroup created")
- // bind security group ingress police
+ // bind securitygroup ingress police
+ Say(state, "Trying to create securitygroup polices", "")
pReq := vpc.NewCreateSecurityGroupPoliciesRequest()
ACCEPT := "ACCEPT"
DEFAULT_CIDR := "0.0.0.0/0"
@@ -70,14 +74,15 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State
},
},
}
- _, err = vpcClient.CreateSecurityGroupPolicies(pReq)
+ err = Retry(ctx, func(ctx context.Context) error {
+ _, e := vpcClient.CreateSecurityGroupPolicies(pReq)
+ return e
+ })
if err != nil {
- ui.Error(fmt.Sprintf("bind security group police failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to create securitygroup polices")
}
- // bind security group engress police
+ // bind securitygroup engress police
pReq = vpc.NewCreateSecurityGroupPoliciesRequest()
pReq.SecurityGroupId = &s.SecurityGroupId
pReq.SecurityGroupPolicySet = &vpc.SecurityGroupPolicySet{
@@ -88,13 +93,16 @@ func (s *stepConfigSecurityGroup) Run(ctx context.Context, state multistep.State
},
},
}
- _, err = vpcClient.CreateSecurityGroupPolicies(pReq)
+ err = Retry(ctx, func(ctx context.Context) error {
+ _, e := vpcClient.CreateSecurityGroupPolicies(pReq)
+ return e
+ })
if err != nil {
- ui.Error(fmt.Sprintf("bind security group police failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to create securitygroup polices")
}
+ Message(state, "Securitygroup polices created", "")
+
return multistep.ActionContinue
}
@@ -102,23 +110,19 @@ func (s *stepConfigSecurityGroup) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
+
ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
- MessageClean(state, "Security Group")
+ SayClean(state, "securitygroup")
+
req := vpc.NewDeleteSecurityGroupRequest()
req.SecurityGroupId = &s.SecurityGroupId
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 5 * time.Second, Multiplier: 2}).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := vpcClient.DeleteSecurityGroup(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := vpcClient.DeleteSecurityGroup(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf("delete security group(%s) failed: %s, you need to delete it by hand",
- s.SecurityGroupId, err.Error()))
- return
+ Error(state, err, fmt.Sprintf("Failed to delete securitygroup(%s), please delete it manually", s.SecurityGroupId))
}
}
diff --git a/builder/tencentcloud/cvm/step_config_subnet.go b/builder/tencentcloud/cvm/step_config_subnet.go
index a200591da..91ab42b7f 100644
--- a/builder/tencentcloud/cvm/step_config_subnet.go
+++ b/builder/tencentcloud/cvm/step_config_subnet.go
@@ -3,12 +3,8 @@ package cvm
import (
"context"
"fmt"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
- "github.com/pkg/errors"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
@@ -22,79 +18,76 @@ type stepConfigSubnet struct {
func (s *stepConfigSubnet) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
+
vpcId := state.Get("vpc_id").(string)
- if len(s.SubnetId) != 0 { // exist subnet
- ui.Say(fmt.Sprintf("Trying to use existing subnet(%s)", s.SubnetId))
+ if len(s.SubnetId) != 0 {
+ Say(state, s.SubnetId, "Trying to use existing subnet")
req := vpc.NewDescribeSubnetsRequest()
req.SubnetIds = []*string{&s.SubnetId}
- resp, err := vpcClient.DescribeSubnets(req)
+ 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 {
- ui.Error(fmt.Sprintf("query subnet failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to get subnet info")
}
if *resp.Response.TotalCount > 0 {
- subnet0 := *resp.Response.SubnetSet[0]
- if *subnet0.VpcId != vpcId {
- message := fmt.Sprintf("the specified subnet(%s) does not belong to "+
- "the specified vpc(%s)", s.SubnetId, vpcId)
- ui.Error(message)
- state.Put("error", errors.New(message))
- return multistep.ActionHalt
- }
- state.Put("subnet_id", *subnet0.SubnetId)
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
}
- message := fmt.Sprintf("the specified subnet(%s) does not exist", s.SubnetId)
- state.Put("error", errors.New(message))
- ui.Error(message)
- return multistep.ActionHalt
- } else { // create a new subnet, tencentcloud create subnet api is synchronous, no need to wait for create.
- ui.Say(fmt.Sprintf("Trying to create a new subnet"))
- req := vpc.NewCreateSubnetRequest()
- req.VpcId = &vpcId
- req.SubnetName = &s.SubnetName
- req.CidrBlock = &s.SubnetCidrBlock
- req.Zone = &s.Zone
- resp, err := vpcClient.CreateSubnet(req)
- if err != nil {
- ui.Error(fmt.Sprintf("create subnet failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
- }
- subnet0 := *resp.Response.Subnet
- state.Put("subnet_id", *subnet0.SubnetId)
- s.SubnetId = *subnet0.SubnetId
- s.isCreate = true
- 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()
+ ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
- MessageClean(state, "SUBNET")
+ SayClean(state, "subnet")
+
req := vpc.NewDeleteSubnetRequest()
req.SubnetId = &s.SubnetId
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 5 * time.Second, Multiplier: 2}).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := vpcClient.DeleteSubnet(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := vpcClient.DeleteSubnet(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf("delete subnet(%s) failed: %s, you need to delete it by hand",
- s.SubnetId, err.Error()))
- return
+ Error(state, err, fmt.Sprintf("Failed to delete subnet(%s), please delete it manually", s.SubnetId))
}
}
diff --git a/builder/tencentcloud/cvm/step_config_vpc.go b/builder/tencentcloud/cvm/step_config_vpc.go
index 46630278a..707afb467 100644
--- a/builder/tencentcloud/cvm/step_config_vpc.go
+++ b/builder/tencentcloud/cvm/step_config_vpc.go
@@ -3,12 +3,8 @@ package cvm
import (
"context"
"fmt"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
- "github.com/pkg/errors"
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
)
@@ -21,69 +17,69 @@ type stepConfigVPC struct {
func (s *stepConfigVPC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
- if len(s.VpcId) != 0 { // exist vpc
- ui.Say(fmt.Sprintf("Trying to use existing vpc(%s)", s.VpcId))
+ if len(s.VpcId) != 0 {
+ Say(state, s.VpcId, "Trying to use existing vpc")
req := vpc.NewDescribeVpcsRequest()
req.VpcIds = []*string{&s.VpcId}
- resp, err := vpcClient.DescribeVpcs(req)
+ 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 {
- ui.Error(fmt.Sprintf("query vpc failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to get vpc info")
}
if *resp.Response.TotalCount > 0 {
- vpc0 := *resp.Response.VpcSet[0]
- state.Put("vpc_id", *vpc0.VpcId)
s.isCreate = false
+ state.Put("vpc_id", *resp.Response.VpcSet[0].VpcId)
+ Message(state, *resp.Response.VpcSet[0].VpcName, "Vpc found")
return multistep.ActionContinue
}
- message := fmt.Sprintf("the specified vpc(%s) does not exist", s.VpcId)
- state.Put("error", errors.New(message))
- ui.Error(message)
- return multistep.ActionHalt
- } else { // create a new vpc, tencentcloud create vpc api is synchronous, no need to wait for create.
- ui.Say(fmt.Sprintf("Trying to create a new vpc"))
- req := vpc.NewCreateVpcRequest()
- req.VpcName = &s.VpcName
- req.CidrBlock = &s.CidrBlock
- resp, err := vpcClient.CreateVpc(req)
- if err != nil {
- ui.Error(fmt.Sprintf("create vpc failed: %s", err.Error()))
- state.Put("error", err)
- return multistep.ActionHalt
- }
- vpc0 := *resp.Response.Vpc
- state.Put("vpc_id", *vpc0.VpcId)
- s.VpcId = *vpc0.VpcId
- s.isCreate = true
- 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()
+ ctx := context.TODO()
vpcClient := state.Get("vpc_client").(*vpc.Client)
- ui := state.Get("ui").(packer.Ui)
- MessageClean(state, "VPC")
+ SayClean(state, "vpc")
+
req := vpc.NewDeleteVpcRequest()
req.VpcId = &s.VpcId
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{InitialBackoff: 5 * time.Second, MaxBackoff: 5 * time.Second, Multiplier: 2}).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := vpcClient.DeleteVpc(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := vpcClient.DeleteVpc(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf("delete vpc(%s) failed: %s, you need to delete it by hand",
- s.VpcId, err.Error()))
- return
+ Error(state, err, fmt.Sprintf("Failed to delete vpc(%s), please delete it manually", s.VpcId))
}
}
diff --git a/builder/tencentcloud/cvm/step_copy_image.go b/builder/tencentcloud/cvm/step_copy_image.go
index e71a9f8cd..b8494f354 100644
--- a/builder/tencentcloud/cvm/step_copy_image.go
+++ b/builder/tencentcloud/cvm/step_copy_image.go
@@ -2,10 +2,9 @@ package cvm
import (
"context"
- "fmt"
+ "strings"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -20,9 +19,11 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
}
client := state.Get("cvm_client").(*cvm.Client)
- ui := state.Get("ui").(packer.Ui)
+
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))
@@ -33,15 +34,17 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
}
req.DestinationRegions = copyRegions
- _, err := client.SyncImages(req)
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.SyncImages(req)
+ return e
+ })
if err != nil {
- state.Put("error", err)
- ui.Error(fmt.Sprintf("copy image failed: %s", err.Error()))
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to copy image")
}
+
+ Message(state, "Image copied", "")
+
return multistep.ActionContinue
}
-func (s *stepCopyImage) Cleanup(state multistep.StateBag) {
- // just do nothing
-}
+func (s *stepCopyImage) Cleanup(state multistep.StateBag) {}
diff --git a/builder/tencentcloud/cvm/step_create_image.go b/builder/tencentcloud/cvm/step_create_image.go
index 1d162fa36..9c4b1e7b3 100644
--- a/builder/tencentcloud/cvm/step_create_image.go
+++ b/builder/tencentcloud/cvm/step_create_image.go
@@ -3,12 +3,8 @@ package cvm
import (
"context"
"fmt"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
- "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -17,17 +13,18 @@ type stepCreateImage struct {
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
- config := state.Get("config").(*Config)
client := state.Get("cvm_client").(*cvm.Client)
- ui := state.Get("ui").(packer.Ui)
+
+ config := state.Get("config").(*Config)
instance := state.Get("instance").(*cvm.Instance)
- ui.Say(fmt.Sprintf("Creating image %s", config.ImageName))
+ 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
@@ -46,51 +43,24 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
req.ForcePoweroff = &False
}
- if config.Reboot {
- req.Reboot = &True
- } else {
- req.Reboot = &False
- }
-
if config.Sysprep {
req.Sysprep = &True
} else {
req.Sysprep = &False
}
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{
- InitialBackoff: 5 * time.Second,
- MaxBackoff: 5 * time.Second,
- Multiplier: 2,
- }).Linear,
- ShouldRetry: func(err error) bool {
- if e, ok := err.(*errors.TencentCloudSDKError); ok {
- if e.Code == "InvalidImageName.Duplicate" {
- return false
- }
- }
- return true
- },
- }.Run(ctx, func(ctx context.Context) error {
- _, err := client.CreateImage(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.CreateImage(req)
+ return e
})
-
if err != nil {
- err := fmt.Errorf("create image failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to create image")
}
+ Message(state, "Waiting for image ready", "")
err = WaitForImageReady(client, config.ImageName, "NORMAL", 3600)
if err != nil {
- err := fmt.Errorf("create image failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to wait for image ready")
}
describeReq := cvm.NewDescribeImagesRequest()
@@ -101,21 +71,24 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
Values: []*string{&config.ImageName},
},
}
- describeResp, err := client.DescribeImages(describeReq)
+
+ var describeResp *cvm.DescribeImagesResponse
+ err = Retry(ctx, func(ctx context.Context) error {
+ var e error
+ describeResp, e = client.DescribeImages(describeReq)
+ return e
+ })
if err != nil {
- err := fmt.Errorf("wait image ready failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to wait for image ready")
}
+
if *describeResp.Response.TotalCount == 0 {
- err := fmt.Errorf("create image(%s) failed", config.ImageName)
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, fmt.Errorf("No image return"), "Failed to crate image")
}
+
s.imageId = *describeResp.Response.ImageSet[0].ImageId
state.Put("image", describeResp.Response.ImageSet[0])
+ Message(state, s.imageId, "Image created")
tencentCloudImages := make(map[string]string)
tencentCloudImages[config.Region] = s.imageId
@@ -128,20 +101,25 @@ 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)
- ui := state.Get("ui").(packer.Ui)
- ui.Say("Delete image because of cancellation or error...")
+ SayClean(state, "image")
+
req := cvm.NewDeleteImagesRequest()
req.ImageIds = []*string{&s.imageId}
- _, err := client.DeleteImages(req)
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.DeleteImages(req)
+ return e
+ })
if err != nil {
- ui.Error(fmt.Sprintf("delete image(%s) failed", s.imageId))
+ Error(state, err, fmt.Sprintf("Failed to delete image(%s), please delete it manually", s.imageId))
}
}
diff --git a/builder/tencentcloud/cvm/step_detach_temp_key_pair.go b/builder/tencentcloud/cvm/step_detach_temp_key_pair.go
index ea1feff73..dd9bc5a1b 100644
--- a/builder/tencentcloud/cvm/step_detach_temp_key_pair.go
+++ b/builder/tencentcloud/cvm/step_detach_temp_key_pair.go
@@ -2,12 +2,8 @@ package cvm
import (
"context"
- "fmt"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -17,35 +13,31 @@ type stepDetachTempKeyPair struct {
func (s *stepDetachTempKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
- instance := state.Get("instance").(*cvm.Instance)
+
if _, ok := state.GetOk("temporary_key_pair_id"); !ok {
return multistep.ActionContinue
}
- keyId := state.Get("temporary_key_pair_id").(*string)
- ui := state.Get("ui").(packer.Ui)
- ui.Say(fmt.Sprintf("Detaching temporary key pair %s...", *keyId))
+
+ 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.KeyIds = []*string{&keyId}
req.InstanceIds = []*string{instance.InstanceId}
req.ForceStop = common.BoolPtr(true)
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{
- InitialBackoff: 5 * time.Second,
- MaxBackoff: 5 * time.Second,
- Multiplier: 2,
- }).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := client.DisassociateInstancesKeyPairs(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.DisassociateInstancesKeyPairs(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf("Fail to detach temporary key pair from instance! Error: %s", err))
- state.Put("error", err)
- return multistep.ActionHalt
+ return Halt(state, err, "Fail to detach keypair from instance")
}
+
+ Message(state, "Keypair detached", "")
+
return multistep.ActionContinue
}
-func (s *stepDetachTempKeyPair) Cleanup(state multistep.StateBag) {
-}
+func (s *stepDetachTempKeyPair) Cleanup(state multistep.StateBag) {}
diff --git a/builder/tencentcloud/cvm/step_pre_validate.go b/builder/tencentcloud/cvm/step_pre_validate.go
index ede5293df..7bee5d9ea 100644
--- a/builder/tencentcloud/cvm/step_pre_validate.go
+++ b/builder/tencentcloud/cvm/step_pre_validate.go
@@ -2,19 +2,45 @@ package cvm
import (
"context"
+ "fmt"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+ cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type stepPreValidate struct {
- DestImageName string
- ForceDelete bool
}
func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
- ui := state.Get("ui").(packer.Ui)
- ui.Say("TencentCloud support images with same name, image_name check is not required.")
+ config := state.Get("config").(*Config)
+ client := state.Get("cvm_client").(*cvm.Client)
+
+ Say(state, config.ImageName, "Trying to check image name")
+
+ req := cvm.NewDescribeImagesRequest()
+ req.Filters = []*cvm.Filter{
+ {
+ Name: common.StringPtr("image-name"),
+ Values: []*string{&config.ImageName},
+ },
+ }
+ 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 images info")
+ }
+
+ if *resp.Response.TotalCount > 0 {
+ return Halt(state, fmt.Errorf("Image name %s has exists", config.ImageName), "")
+ }
+
+ Message(state, "useable", "Image name")
+
return multistep.ActionContinue
}
diff --git a/builder/tencentcloud/cvm/step_run_instance.go b/builder/tencentcloud/cvm/step_run_instance.go
index 224a62425..b43e1fbf6 100644
--- a/builder/tencentcloud/cvm/step_run_instance.go
+++ b/builder/tencentcloud/cvm/step_run_instance.go
@@ -6,11 +6,8 @@ import (
"fmt"
"io/ioutil"
"log"
- "time"
- "github.com/hashicorp/packer/common/retry"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -32,8 +29,8 @@ type stepRunInstance struct {
func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("cvm_client").(*cvm.Client)
+
config := state.Get("config").(*Config)
- ui := state.Get("ui").(packer.Ui)
source_image := state.Get("source_image").(*cvm.Image)
vpc_id := state.Get("vpc_id").(string)
subnet_id := state.Get("subnet_id").(string)
@@ -43,15 +40,14 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul
if password == "" && config.Comm.WinRMPassword != "" {
password = config.Comm.WinRMPassword
}
+
userData, err := s.getUserData(state)
if err != nil {
- err := fmt.Errorf("get user_data failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to get user_data")
}
- ui.Say("Creating Instance.")
+ Say(state, "Trying to create a new instance", "")
+
// config RunInstances parameters
POSTPAID_BY_HOUR := "POSTPAID_BY_HOUR"
req := cvm.NewRunInstancesRequest()
@@ -71,7 +67,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul
// 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 {
- ui.Say("Use source image snapshot data disks, ignore user data disk settings.")
+ 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" {
@@ -144,44 +140,49 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul
}
}
- resp, err := client.RunInstances(req)
+ 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 {
- err := fmt.Errorf("create instance failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to run instance")
}
+
if len(resp.Response.InstanceIdSet) != 1 {
- err := fmt.Errorf("create instance failed: %d instance(s) created", len(resp.Response.InstanceIdSet))
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ 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(client, s.instanceId, "RUNNING", 1800)
if err != nil {
- err := fmt.Errorf("wait instance launch failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to wait for instance ready")
}
describeReq := cvm.NewDescribeInstancesRequest()
describeReq.InstanceIds = []*string{&s.instanceId}
- describeResp, err := client.DescribeInstances(describeReq)
+ 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 {
- err := fmt.Errorf("wait instance launch failed: %s", err.Error())
- state.Put("error", err)
- ui.Error(err.Error())
- return multistep.ActionHalt
+ return Halt(state, err, "Failed to wait for instance ready")
}
+
state.Put("instance", describeResp.Response.InstanceSet[0])
+ 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 {
@@ -189,8 +190,10 @@ func (s *stepRunInstance) getUserData(state multistep.StateBag) (string, error)
}
userData = string(data)
}
+
userData = base64.StdEncoding.EncodeToString([]byte(userData))
- log.Printf(fmt.Sprintf("user_data: %s", userData))
+ log.Printf(fmt.Sprintf("[DEBUG]getUserData: user_data: %s", userData))
+
return userData, nil
}
@@ -198,24 +201,19 @@ func (s *stepRunInstance) Cleanup(state multistep.StateBag) {
if s.instanceId == "" {
return
}
- MessageClean(state, "Instance")
+
+ ctx := context.TODO()
client := state.Get("cvm_client").(*cvm.Client)
- ui := state.Get("ui").(packer.Ui)
+
+ SayClean(state, "instance")
+
req := cvm.NewTerminateInstancesRequest()
req.InstanceIds = []*string{&s.instanceId}
- ctx := context.TODO()
- err := retry.Config{
- Tries: 60,
- RetryDelay: (&retry.Backoff{
- InitialBackoff: 5 * time.Second,
- MaxBackoff: 5 * time.Second,
- Multiplier: 2,
- }).Linear,
- }.Run(ctx, func(ctx context.Context) error {
- _, err := client.TerminateInstances(req)
- return err
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.TerminateInstances(req)
+ return e
})
if err != nil {
- ui.Error(fmt.Sprintf("terminate instance(%s) failed: %s", s.instanceId, err.Error()))
+ Error(state, err, fmt.Sprintf("Failed to terminate instance(%s), please delete it manually", s.instanceId))
}
}
diff --git a/builder/tencentcloud/cvm/step_share_image.go b/builder/tencentcloud/cvm/step_share_image.go
index ffbe3ab9a..f5d4986b8 100644
--- a/builder/tencentcloud/cvm/step_share_image.go
+++ b/builder/tencentcloud/cvm/step_share_image.go
@@ -3,9 +3,10 @@ package cvm
import (
"context"
"fmt"
+ "strings"
"github.com/hashicorp/packer/helper/multistep"
- "github.com/hashicorp/packer/packer"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
@@ -19,50 +20,57 @@ func (s *stepShareImage) Run(ctx context.Context, state multistep.StateBag) mult
}
client := state.Get("cvm_client").(*cvm.Client)
- ui := state.Get("ui").(packer.Ui)
+
imageId := state.Get("image").(*cvm.Image).ImageId
+ Say(state, strings.Join(s.ShareAccounts, ","), "Trying to share image to")
req := cvm.NewModifyImageSharePermissionRequest()
req.ImageId = imageId
- SHARE := "SHARE"
- req.Permission = &SHARE
+ req.Permission = common.StringPtr("SHARE")
accounts := make([]*string, 0, len(s.ShareAccounts))
for _, account := range s.ShareAccounts {
accounts = append(accounts, &account)
}
req.AccountIds = accounts
-
- _, err := client.ModifyImageSharePermission(req)
+ err := Retry(ctx, func(ctx context.Context) error {
+ _, e := client.ModifyImageSharePermission(req)
+ return e
+ })
if err != nil {
- state.Put("error", err)
- ui.Error(fmt.Sprintf("share image failed: %s", err.Error()))
- return multistep.ActionHalt
+ 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 {
- ui := state.Get("ui").(packer.Ui)
- client := state.Get("cvm_client").(*cvm.Client)
- imageId := state.Get("image").(*cvm.Image).ImageId
- ui.Say("Cancel share image due to action cancelled or halted.")
+ if !cancelled && !halted {
+ return
+ }
+
+ ctx := context.TODO()
+ client := state.Get("cvm_client").(*cvm.Client)
- req := cvm.NewModifyImageSharePermissionRequest()
- req.ImageId = imageId
- CANCEL := "CANCEL"
- req.Permission = &CANCEL
- accounts := make([]*string, 0, len(s.ShareAccounts))
- for _, account := range s.ShareAccounts {
- accounts = append(accounts, &account)
- }
- req.AccountIds = accounts
+ imageId := state.Get("image").(*cvm.Image).ImageId
+ SayClean(state, "image share")
- _, err := client.ModifyImageSharePermission(req)
- if err != nil {
- ui.Error(fmt.Sprintf("Cancel share image failed: %s", err.Error()))
- }
+ 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))
}
}
diff --git a/examples/tencentcloud/basic-with-data-disk.json b/examples/tencentcloud/basic-with-data-disk.json
index 695a68014..9f78afdbe 100644
--- a/examples/tencentcloud/basic-with-data-disk.json
+++ b/examples/tencentcloud/basic-with-data-disk.json
@@ -3,32 +3,38 @@
"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"
- },
- "data_disks": [{
- "disk_type": "CLOUD_PREMIUM",
- "disk_size": 50
- }]
- }],
- "provisioners": [{
- "type": "shell",
- "inline": [
- "sleep 30",
- "yum install redis.x86_64 -y"
- ]
- }]
-}
+ "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"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/tencentcloud/basic.json b/examples/tencentcloud/basic.json
index 4e90efd36..09663ebbc 100644
--- a/examples/tencentcloud/basic.json
+++ b/examples/tencentcloud/basic.json
@@ -3,28 +3,32 @@
"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"
+ "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"
- ]
- }]
-}
+ ],
+ "provisioners": [
+ {
+ "type": "shell",
+ "inline": [
+ "sleep 30",
+ "yum install redis.x86_64 -y"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/examples/tencentcloud/centos.json b/examples/tencentcloud/centos.json
index 6b4d13bd5..1d55f3fe7 100644
--- a/examples/tencentcloud/centos.json
+++ b/examples/tencentcloud/centos.json
@@ -3,33 +3,40 @@
"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",
- "vpc_id": "vpc-gjusx3kd",
- "subnet_id": "subnet-pfditepm",
- "internet_max_bandwidth_out": 2,
- "security_group_id": "sg-rypoiksl",
- "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
- }]
+ "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
+ }
+ ]
}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index e75f6325c..fb45b907b 100644
--- a/go.mod
+++ b/go.mod
@@ -132,7 +132,7 @@ require (
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect
github.com/stretchr/testify v1.4.0
github.com/temoto/robotstxt v1.1.1 // indirect
- github.com/tencentcloud/tencentcloud-sdk-go v3.0.71+incompatible
+ github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible
github.com/ucloud/ucloud-sdk-go v0.8.7
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
github.com/ulikunitz/xz v0.5.5
diff --git a/go.sum b/go.sum
index e3e08c8ce..e343bae66 100644
--- a/go.sum
+++ b/go.sum
@@ -395,8 +395,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA=
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
-github.com/tencentcloud/tencentcloud-sdk-go v3.0.71+incompatible h1:9sIWfe6ZC7xoSlshYWNGicPqomK7N+CsHMa1YFWBCWU=
-github.com/tencentcloud/tencentcloud-sdk-go v3.0.71+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
+github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible h1:G8i7dPMK1RCpbQz+VpfFp679vmVna38NI8tz5xsybXI=
+github.com/tencentcloud/tencentcloud-sdk-go v3.0.94+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/ucloud/ucloud-sdk-go v0.8.7 h1:BmXOb5RivI0Uu4oZRpjI6SQ9/y7n/H9wxTGR1txIE8o=
github.com/ucloud/ucloud-sdk-go v0.8.7/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go
index 92b2d7175..96a9644b1 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go
@@ -6,10 +6,12 @@ import (
"fmt"
"log"
"net/http"
+ "net/http/httputil"
"strconv"
"strings"
"time"
+ "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
)
@@ -18,6 +20,7 @@ type Client struct {
region string
httpClient *http.Client
httpProfile *profile.HttpProfile
+ profile *profile.ClientProfile
credential *Credential
signMethod string
unsignedPayload bool
@@ -47,6 +50,8 @@ func (c *Client) Send(request tchttp.Request, response tchttp.Response) (err err
}
func (c *Client) sendWithSignatureV1(request tchttp.Request, response tchttp.Response) (err error) {
+ // TODO: not an elegant way, it should be done in common params, but finally it need to refactor
+ request.GetParams()["Language"] = c.profile.Language
err = tchttp.ConstructParams(request)
if err != nil {
return err
@@ -62,10 +67,18 @@ func (c *Client) sendWithSignatureV1(request tchttp.Request, response tchttp.Res
if request.GetHttpMethod() == "POST" {
httpRequest.Header["Content-Type"] = []string{"application/x-www-form-urlencoded"}
}
- //log.Printf("[DEBUG] http request=%v", httpRequest)
+ if c.debug {
+ outbytes, err := httputil.DumpRequest(httpRequest, true)
+ if err != nil {
+ log.Printf("[ERROR] dump request failed because %s", err)
+ return err
+ }
+ log.Printf("[DEBUG] http request = %s", outbytes)
+ }
httpResponse, err := c.httpClient.Do(httpRequest)
if err != nil {
- return err
+ msg := fmt.Sprintf("Fail to get response because %s", err)
+ return errors.NewTencentCloudSDKError("ClientError.NetworkError", msg, "")
}
err = tchttp.ParseFromHttpResponse(httpResponse, response)
return err
@@ -78,6 +91,7 @@ func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Res
"X-TC-Version": request.GetVersion(),
"X-TC-Timestamp": request.GetParams()["Timestamp"],
"X-TC-RequestClient": request.GetParams()["RequestClient"],
+ "X-TC-Language": c.profile.Language,
}
if c.region != "" {
headers["X-TC-Region"] = c.region
@@ -184,10 +198,18 @@ func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Res
for k, v := range headers {
httpRequest.Header[k] = []string{v}
}
- //log.Printf("[DEBUG] http request=%v, body=%v", httpRequest, requestPayload)
+ if c.debug {
+ outbytes, err := httputil.DumpRequest(httpRequest, true)
+ if err != nil {
+ log.Printf("[ERROR] dump request failed because %s", err)
+ return err
+ }
+ log.Printf("[DEBUG] http request = %s", outbytes)
+ }
httpResponse, err := c.httpClient.Do(httpRequest)
if err != nil {
- return err
+ msg := fmt.Sprintf("Fail to get response because %s", err)
+ return errors.NewTencentCloudSDKError("ClientError.NetworkError", msg, "")
}
err = tchttp.ParseFromHttpResponse(httpResponse, response)
return err
@@ -200,7 +222,7 @@ func (c *Client) GetRegion() string {
func (c *Client) Init(region string) *Client {
c.httpClient = &http.Client{}
c.region = region
- c.signMethod = "HmacSHA256"
+ c.signMethod = "TC3-HMAC-SHA256"
c.debug = false
log.SetFlags(log.LstdFlags | log.Lshortfile)
return c
@@ -217,6 +239,7 @@ func (c *Client) WithCredential(cred *Credential) *Client {
}
func (c *Client) WithProfile(clientProfile *profile.ClientProfile) *Client {
+ c.profile = clientProfile
c.signMethod = clientProfile.SignMethod
c.unsignedPayload = clientProfile.UnsignedPayload
c.httpProfile = clientProfile.HttpProfile
@@ -229,7 +252,7 @@ func (c *Client) WithSignatureMethod(method string) *Client {
return c
}
-func (c *Client) WithHttpTransport (transport http.RoundTripper) *Client {
+func (c *Client) WithHttpTransport(transport http.RoundTripper) *Client {
c.httpClient.Transport = transport
return c
}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go
index 4774013fa..cdfbe7ec1 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go
@@ -152,7 +152,7 @@ func CompleteCommonParams(request Request, region string) {
params["Action"] = request.GetAction()
params["Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
params["Nonce"] = strconv.Itoa(rand.Int())
- params["RequestClient"] = "SDK_GO_3.0.71"
+ params["RequestClient"] = "SDK_GO_3.0.94"
}
func ConstructParams(req Request) (err error) {
@@ -195,8 +195,9 @@ func flatStructure(value reflect.Value, request Request, prefix string) (err err
} else if kind == reflect.Float64 {
request.GetParams()[key] = strconv.FormatFloat(field.Float(), 'f', -1, 64)
} else if kind == reflect.Slice {
- for j := 0; j < field.Len(); j++ {
- vj := field.Index(j)
+ list := value.Field(i)
+ for j := 0; j < list.Len(); j++ {
+ vj := list.Index(j)
key := prefix + nameTag + "." + strconv.Itoa(j)
kind = vj.Kind()
if kind == reflect.Ptr && vj.IsNil() {
@@ -217,11 +218,15 @@ func flatStructure(value reflect.Value, request Request, prefix string) (err err
} else if kind == reflect.Float64 {
request.GetParams()[key] = strconv.FormatFloat(vj.Float(), 'f', -1, 64)
} else {
- return flatStructure(vj, request, key+".")
+ if err = flatStructure(vj, request, key+"."); err != nil {
+ return
+ }
}
}
} else {
- return flatStructure(reflect.ValueOf(field.Interface()), request, prefix+nameTag+".")
+ if err = flatStructure(reflect.ValueOf(field.Interface()), request, prefix+nameTag+"."); err != nil {
+ return
+ }
}
}
return
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go
index c8e27f7f5..288f21bdf 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
- "log"
+ //"log"
"net/http"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
@@ -37,7 +37,8 @@ func (r *BaseResponse) ParseErrorFromHTTPResponse(body []byte) (err error) {
resp := &ErrorResponse{}
err = json.Unmarshal(body, resp)
if err != nil {
- return
+ msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+ return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if resp.Response.Error.Code != "" {
return errors.NewTencentCloudSDKError(resp.Response.Error.Code, resp.Response.Error.Message, resp.Response.RequestId)
@@ -46,7 +47,8 @@ func (r *BaseResponse) ParseErrorFromHTTPResponse(body []byte) (err error) {
deprecated := &DeprecatedAPIErrorResponse{}
err = json.Unmarshal(body, deprecated)
if err != nil {
- return
+ msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+ return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
if deprecated.Code != 0 {
return errors.NewTencentCloudSDKError(deprecated.CodeDesc, deprecated.Message, "")
@@ -58,10 +60,12 @@ func ParseFromHttpResponse(hr *http.Response, response Response) (err error) {
defer hr.Body.Close()
body, err := ioutil.ReadAll(hr.Body)
if err != nil {
- return
+ msg := fmt.Sprintf("Fail to read response body because %s", err)
+ return errors.NewTencentCloudSDKError("ClientError.IOError", msg, "")
}
if hr.StatusCode != 200 {
- return fmt.Errorf("Request fail with status: %s, with body: %s", hr.Status, body)
+ msg := fmt.Sprintf("Request fail with http status code: %s, with body: %s", hr.Status, body)
+ return errors.NewTencentCloudSDKError("ClientError.HttpStatusCodeError", msg, "")
}
//log.Printf("[DEBUG] Response Body=%s", body)
err = response.ParseErrorFromHTTPResponse(body)
@@ -70,7 +74,8 @@ func ParseFromHttpResponse(hr *http.Response, response Response) (err error) {
}
err = json.Unmarshal(body, &response)
if err != nil {
- log.Printf("Unexpected Error occurs when parsing API response\n%s\n", string(body[:]))
+ msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+ return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
}
return
}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go
index 0fc71750b..21069ff99 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go
@@ -1,9 +1,14 @@
package profile
type ClientProfile struct {
- HttpProfile *HttpProfile
+ HttpProfile *HttpProfile
+ // Valid choices: HmacSHA1, HmacSHA256, TC3-HMAC-SHA256.
+ // Default value is TC3-HMAC-SHA256.
SignMethod string
UnsignedPayload bool
+ // Valid choices: zh-CN, en-US.
+ // Default value is zh-CN.
+ Language string
}
func NewClientProfile() *ClientProfile {
@@ -11,5 +16,6 @@ func NewClientProfile() *ClientProfile {
HttpProfile: NewHttpProfile(),
SignMethod: "TC3-HMAC-SHA256",
UnsignedPayload: false,
+ Language: "zh-CN",
}
}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go
index 8d4bf8f57..cf633a8a0 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go
@@ -4,7 +4,9 @@ type HttpProfile struct {
ReqMethod string
ReqTimeout int
Endpoint string
- Protocol string
+ // Deprecated, use Scheme instead
+ Protocol string
+ Scheme string
}
func NewHttpProfile() *HttpProfile {
@@ -12,6 +14,6 @@ func NewHttpProfile() *HttpProfile {
ReqMethod: "POST",
ReqTimeout: 60,
Endpoint: "",
- Protocol: "HTTPS",
+ Scheme: "HTTPS",
}
}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go
index 63efd9227..0aa7b7355 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go
@@ -1,14 +1,13 @@
package common
import (
+ "bytes"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "fmt"
"sort"
- "strings"
tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
)
@@ -65,7 +64,11 @@ func getStringToSign(request tchttp.Request) string {
domain := request.GetDomain()
path := request.GetPath()
- text := method + domain + path + "?"
+ var buf bytes.Buffer
+ buf.WriteString(method)
+ buf.WriteString(domain)
+ buf.WriteString(path)
+ buf.WriteString("?")
params := request.GetParams()
// sort params
@@ -77,11 +80,15 @@ func getStringToSign(request tchttp.Request) string {
for i := range keys {
k := keys[i]
+ // TODO: check if server side allows empty value in url.
if params[k] == "" {
continue
}
- text += fmt.Sprintf("%v=%v&", strings.Replace(k, "_", ".", -1), params[k])
+ buf.WriteString(k)
+ buf.WriteString("=")
+ buf.WriteString(params[k])
+ buf.WriteString("&")
}
- text = text[:len(text)-1]
- return text
+ buf.Truncate(buf.Len() - 1)
+ return buf.String()
}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/client.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/client.go
index 3305127e1..24f87ebaf 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/client.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/client.go
@@ -743,6 +743,56 @@ func (c *Client) DescribeRegions(request *DescribeRegionsRequest) (response *Des
return
}
+func NewDescribeReservedInstancesRequest() (request *DescribeReservedInstancesRequest) {
+ request = &DescribeReservedInstancesRequest{
+ BaseRequest: &tchttp.BaseRequest{},
+ }
+ request.Init().WithApiInfo("cvm", APIVersion, "DescribeReservedInstances")
+ return
+}
+
+func NewDescribeReservedInstancesResponse() (response *DescribeReservedInstancesResponse) {
+ response = &DescribeReservedInstancesResponse{
+ BaseResponse: &tchttp.BaseResponse{},
+ }
+ return
+}
+
+// 本接口(DescribeReservedInstances)可提供列出用户已购买的预留实例
+func (c *Client) DescribeReservedInstances(request *DescribeReservedInstancesRequest) (response *DescribeReservedInstancesResponse, err error) {
+ if request == nil {
+ request = NewDescribeReservedInstancesRequest()
+ }
+ response = NewDescribeReservedInstancesResponse()
+ err = c.Send(request, response)
+ return
+}
+
+func NewDescribeReservedInstancesOfferingsRequest() (request *DescribeReservedInstancesOfferingsRequest) {
+ request = &DescribeReservedInstancesOfferingsRequest{
+ BaseRequest: &tchttp.BaseRequest{},
+ }
+ request.Init().WithApiInfo("cvm", APIVersion, "DescribeReservedInstancesOfferings")
+ return
+}
+
+func NewDescribeReservedInstancesOfferingsResponse() (response *DescribeReservedInstancesOfferingsResponse) {
+ response = &DescribeReservedInstancesOfferingsResponse{
+ BaseResponse: &tchttp.BaseResponse{},
+ }
+ return
+}
+
+// 本接口(DescribeReservedInstancesOfferings)供用户列出可购买的预留实例配置
+func (c *Client) DescribeReservedInstancesOfferings(request *DescribeReservedInstancesOfferingsRequest) (response *DescribeReservedInstancesOfferingsResponse, err error) {
+ if request == nil {
+ request = NewDescribeReservedInstancesOfferingsRequest()
+ }
+ response = NewDescribeReservedInstancesOfferingsResponse()
+ err = c.Send(request, response)
+ return
+}
+
func NewDescribeZoneInstanceConfigInfosRequest() (request *DescribeZoneInstanceConfigInfosRequest) {
request = &DescribeZoneInstanceConfigInfosRequest{
BaseRequest: &tchttp.BaseRequest{},
@@ -863,7 +913,7 @@ func NewImportImageResponse() (response *ImportImageResponse) {
return
}
-// 本接口(ImportImage)用于导入镜像,导入后的镜像可用于创建实例。
+// 本接口(ImportImage)用于导入镜像,导入后的镜像可用于创建实例。
func (c *Client) ImportImage(request *ImportImageRequest) (response *ImportImageResponse, err error) {
if request == nil {
request = NewImportImageRequest()
@@ -972,7 +1022,11 @@ func NewInquiryPriceResetInstanceResponse() (response *InquiryPriceResetInstance
return
}
-// 本接口 (InquiryPriceResetInstance) 用于重装实例询价。* 如果指定了`ImageId`参数,则使用指定的镜像进行重装询价;否则按照当前实例使用的镜像进行重装询价。* 目前只支持[系统盘类型](/document/api/213/9452#block_device)是`CLOUD_BASIC`、`CLOUD_PREMIUM`、`CLOUD_SSD`类型的实例使用该接口实现`Linux`和`Windows`操作系统切换的重装询价。* 目前不支持海外地域的实例使用该接口实现`Linux`和`Windows`操作系统切换的重装询价。
+// 本接口 (InquiryPriceResetInstance) 用于重装实例询价。
+//
+// * 如果指定了`ImageId`参数,则使用指定的镜像进行重装询价;否则按照当前实例使用的镜像进行重装询价。
+// * 目前只支持[系统盘类型](/document/api/213/9452#block_device)是`CLOUD_BASIC`、`CLOUD_PREMIUM`、`CLOUD_SSD`类型的实例使用该接口实现`Linux`和`Windows`操作系统切换的重装询价。
+// * 目前不支持海外地域的实例使用该接口实现`Linux`和`Windows`操作系统切换的重装询价。
func (c *Client) InquiryPriceResetInstance(request *InquiryPriceResetInstanceRequest) (response *InquiryPriceResetInstanceResponse, err error) {
if request == nil {
request = NewInquiryPriceResetInstanceRequest()
@@ -1374,6 +1428,31 @@ func (c *Client) ModifyKeyPairAttribute(request *ModifyKeyPairAttributeRequest)
return
}
+func NewPurchaseReservedInstancesOfferingRequest() (request *PurchaseReservedInstancesOfferingRequest) {
+ request = &PurchaseReservedInstancesOfferingRequest{
+ BaseRequest: &tchttp.BaseRequest{},
+ }
+ request.Init().WithApiInfo("cvm", APIVersion, "PurchaseReservedInstancesOffering")
+ return
+}
+
+func NewPurchaseReservedInstancesOfferingResponse() (response *PurchaseReservedInstancesOfferingResponse) {
+ response = &PurchaseReservedInstancesOfferingResponse{
+ BaseResponse: &tchttp.BaseResponse{},
+ }
+ return
+}
+
+// 本接口(PurchaseReservedInstancesOffering)用于用户购买一个或者多个指定配置的预留实例
+func (c *Client) PurchaseReservedInstancesOffering(request *PurchaseReservedInstancesOfferingRequest) (response *PurchaseReservedInstancesOfferingResponse, err error) {
+ if request == nil {
+ request = NewPurchaseReservedInstancesOfferingRequest()
+ }
+ response = NewPurchaseReservedInstancesOfferingResponse()
+ err = c.Send(request, response)
+ return
+}
+
func NewRebootInstancesRequest() (request *RebootInstancesRequest) {
request = &RebootInstancesRequest{
BaseRequest: &tchttp.BaseRequest{},
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/models.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/models.go
index aef3f7b30..d159bc4e2 100644
--- a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/models.go
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312/models.go
@@ -243,19 +243,17 @@ type CreateImageRequest struct {
// 镜像描述
ImageDescription *string `json:"ImageDescription,omitempty" name:"ImageDescription"`
- // 软关机失败时是否执行强制关机以制作镜像
+ // 是否执行强制关机以制作镜像。
+ // 取值范围:
按照预留实例计费可购买的【可用区】进行过滤。形如:ap-guangzhou-1。
类型:String
必选:否
可选项:可用区列表
+ //按照预留实例计费【有效期】即预留实例计费购买时长进行过滤。形如:31536000。
类型:Integer
计量单位:秒
必选:否
可选项:31536000 (1年) | 94608000(3年)
+ //按照【预留实例计费类型】进行过滤。形如:S3.MEDIUM4。
类型:String
必选:否
可选项:预留实例计费类型列表
+ //按照【付款类型】进行过滤。形如:All Upfront (预付全部费用)。
类型:String
必选:否
可选项:All Upfront (预付全部费用)
+ //按照预留实例计费的【平台描述】(即操作系统)进行过滤。形如:linux。
类型:String
必选:否
可选项:linux
+ //按照【预留实例计费配置ID】进行过滤。形如:650c138f-ae7e-4750-952a-96841d6e9fc1。
类型:String
必选:否
+ // 每次请求的`Filters`的上限为10,`Filter.Values`的上限为5。 + Filters []*Filter `json:"Filters,omitempty" name:"Filters" list` +} + +func (r *DescribeReservedInstancesOfferingsRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +func (r *DescribeReservedInstancesOfferingsRequest) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +type DescribeReservedInstancesOfferingsResponse struct { + *tchttp.BaseResponse + Response *struct { + + // 符合条件的预留实例计费数量。 + TotalCount *int64 `json:"TotalCount,omitempty" name:"TotalCount"` + + // 符合条件的预留实例计费列表。 + ReservedInstancesOfferingsSet []*ReservedInstancesOffering `json:"ReservedInstancesOfferingsSet,omitempty" name:"ReservedInstancesOfferingsSet" list` + + // 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + } `json:"Response"` +} + +func (r *DescribeReservedInstancesOfferingsResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +func (r *DescribeReservedInstancesOfferingsResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +type DescribeReservedInstancesRequest struct { + *tchttp.BaseRequest + + // 试运行。默认为 false。 + DryRun *bool `json:"DryRun,omitempty" name:"DryRun"` + + // 偏移量,默认为0。关于`Offset`的更进一步介绍请参考 API [简介](https://cloud.tencent.com/document/api/213/15688)中的相关小节。 + Offset *int64 `json:"Offset,omitempty" name:"Offset"` + + // 返回数量,默认为20,最大值为100。关于`Limit`的更进一步介绍请参考 API [简介](https://cloud.tencent.com/document/api/213/15688)中的相关小节。 + Limit *int64 `json:"Limit,omitempty" name:"Limit"` + + //按照预留实例计费可购买的【可用区】进行过滤。形如:ap-guangzhou-1。
类型:String
必选:否
可选项:可用区列表
+ //按照预留实例计费【有效期】即预留实例计费购买时长进行过滤。形如:31536000。
类型:Integer
计量单位:秒
必选:否
可选项:31536000 (1年) | 94608000(3年)
+ //按照【预留实例计费类型】进行过滤。形如:S3.MEDIUM4。
类型:String
必选:否
可选项:预留实例计费类型列表
+ //按照【付款类型】进行过滤。形如:All Upfront (预付全部费用)。
类型:String
必选:否
可选项:All Upfront (预付全部费用)
+ //按照预留实例计费的【平台描述】(即操作系统)进行过滤。形如:linux。
类型:String
必选:否
可选项:linux
+ //按照已购买【预留实例计费ID】进行过滤。形如:650c138f-ae7e-4750-952a-96841d6e9fc1。
类型:String
必选:否
+ //按照已购买【预留实例计费状态】进行过滤。形如:active。
类型:String
必选:否
可选项:active (以创建) | pending (等待被创建) | retired (过期)
+ // 每次请求的`Filters`的上限为10,`Filter.Values`的上限为5。 + Filters []*Filter `json:"Filters,omitempty" name:"Filters" list` +} + +func (r *DescribeReservedInstancesRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +func (r *DescribeReservedInstancesRequest) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +type DescribeReservedInstancesResponse struct { + *tchttp.BaseResponse + Response *struct { + + // 符合条件的预留实例计费数量。 + TotalCount *int64 `json:"TotalCount,omitempty" name:"TotalCount"` + + // 符合条件的预留实例计费列表。 + ReservedInstancesSet []*ReservedInstances `json:"ReservedInstancesSet,omitempty" name:"ReservedInstancesSet" list` + + // 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitempty" name:"RequestId"` + } `json:"Response"` +} + +func (r *DescribeReservedInstancesResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +func (r *DescribeReservedInstancesResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + type DescribeZoneInstanceConfigInfosRequest struct { *tchttp.BaseRequest @@ -2010,7 +2145,7 @@ type Instance struct { // 实例名称。 InstanceName *string `json:"InstanceName,omitempty" name:"InstanceName"` - // 实例计费模式。取值范围: