Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 257 additions & 9 deletions tencentcloud/resource_tc_instance_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func resourceTencentCloudInstanceSet() *schema.Resource {
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(600 * time.Second),
Read: schema.DefaultTimeout(600 * time.Second),
Update: schema.DefaultTimeout(600 * time.Second),
Delete: schema.DefaultTimeout(600 * time.Second),
},

Expand All @@ -110,9 +111,16 @@ func resourceTencentCloudInstanceSet() *schema.Resource {
"instance_count": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Description: "The number of instances to be purchased. Value range:[1,100]; default value: 1.",
},
"exclude_instance_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "instance ids list to exclude.",
},
"instance_name": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -361,10 +369,24 @@ func resourceTencentCloudInstanceSetRead(d *schema.ResourceData, meta interface{
}
}

func resourceTencentCloudInstanceSetUpdate(d *schema.ResourceData, meta interface{}) (err error) {
defer logElapsed("resource.tencentcloud_instance_set.update")()
func resourceTencentCloudInstanceSetUpdate(d *schema.ResourceData, meta interface{}) error {
doneChan := make(chan struct{}, 1)
rspChan := make(chan error, 1)

timeout := d.Timeout(schema.TimeoutUpdate)

go func(d *schema.ResourceData, meta interface{}) {
e := doResourceTencentCloudInstanceSetUpdate(d, meta)
doneChan <- struct{}{}
rspChan <- e
}(d, meta)

return fmt.Errorf("`resource_instance_set` do not support change now.")
select {
case <-doneChan:
return <-rspChan
case <-time.After(timeout):
return fmt.Errorf("Do cvm instance set update action timeout, current timeout :[%.3f]s", timeout.Seconds())
}
}

func resourceTencentCloudInstanceSetDelete(d *schema.ResourceData, meta interface{}) error {
Expand Down Expand Up @@ -559,15 +581,18 @@ func doResourceTencentCloudInstanceSetRead(d *schema.ResourceData, meta interfac
logId := getLogId(contextNil)
ctx := context.WithValue(context.TODO(), logIdKey, logId)

instanceId := d.Id()
var instanceSetIds []*string
if v, ok := d.GetOk("instance_ids"); ok {
instanceSetIds = helper.InterfacesStringsPoint(v.([]interface{}))
}

client := meta.(*TencentCloudClient).apiV3Conn
cvmService := CvmService{
client: client,
}
var instanceSet []*cvm.Instance
var errRet error
instanceSet, errRet = cvmService.DescribeInstanceSetByIds(ctx, instanceId)
instanceSet, errRet = cvmService.DescribeInstanceSetByIds(ctx, helper.StrListToStr(instanceSetIds))
if errRet != nil {
return errRet
}
Expand All @@ -579,7 +604,6 @@ func doResourceTencentCloudInstanceSetRead(d *schema.ResourceData, meta interfac

instance := instanceSet[0]

_ = d.Set("instance_count", len(instanceSet))
_ = d.Set("image_id", instance.ImageId)
_ = d.Set("availability_zone", instance.Placement.Zone)
_ = d.Set("instance_name", d.Get("instance_name"))
Expand Down Expand Up @@ -621,21 +645,245 @@ func doResourceTencentCloudInstanceSetRead(d *schema.ResourceData, meta interfac
return nil
}

func doResourceTencentCloudInstanceSetUpdate(d *schema.ResourceData, meta interface{}) (err error) {
defer logElapsed("resource.tencentcloud_instance_set.update")()

logId := getLogId(contextNil)
ctx := context.WithValue(context.TODO(), logIdKey, logId)

cvmService := CvmService{
client: meta.(*TencentCloudClient).apiV3Conn,
}

if d.HasChange("exclude_instance_ids") {
old, new := d.GetChange("exclude_instance_ids")
olds := old.(*schema.Set)
news := new.(*schema.Set)
needExclude := news.Difference(olds).List()
needCreate := olds.Difference(news).List()

// need delete instance
if len(needExclude) > 0 {
instanceSetIds := helper.StrListToStr(helper.InterfacesStringsPoint(needExclude))
err := resource.Retry(writeRetryTimeout, func() *resource.RetryError {
errRet := cvmService.DeleteInstanceSetByIds(ctx, instanceSetIds)
if errRet != nil {
log.Printf("[CRITAL][first delete]%s api[%s] fail, reason[%s]\n",
logId, "delete", errRet.Error())
e, ok := errRet.(*sdkErrors.TencentCloudSDKError)
if ok && IsContains(CVM_RETRYABLE_ERROR, e.Code) {
time.Sleep(1 * time.Second) // 需要重试的话,等待1s进行重试
return resource.RetryableError(fmt.Errorf("[first delete]cvm delete error: %s, retrying", e.Error()))
}
return resource.NonRetryableError(errRet)
}
return nil
})
if err != nil {
return err
}
}

createInstanceIds := make([]*string, 0)
// need create instance
if len(needCreate) > 0 {
instanceCount := len(needCreate)
request := cvm.NewRunInstancesRequest()
request.ImageId = helper.String(d.Get("image_id").(string))
request.InstanceCount = helper.Int64(int64(instanceCount))
request.Placement = &cvm.Placement{
Zone: helper.String(d.Get("availability_zone").(string)),
}
if v, ok := d.GetOk("project_id"); ok {
projectId := int64(v.(int))
request.Placement.ProjectId = &projectId
}
if v, ok := d.GetOk("instance_name"); ok {
request.InstanceName = helper.String(v.(string))
}

if v, ok := d.GetOk("instance_type"); ok {
request.InstanceType = helper.String(v.(string))
}
if v, ok := d.GetOk("hostname"); ok {
request.HostName = helper.String(v.(string))
}
if v, ok := d.GetOk("cam_role_name"); ok {
request.CamRoleName = helper.String(v.(string))
}

if v, ok := d.GetOk("instance_charge_type"); ok {
instanceChargeType := v.(string)
request.InstanceChargeType = &instanceChargeType
}
if v, ok := d.GetOk("placement_group_id"); ok {
request.DisasterRecoverGroupIds = []*string{helper.String(v.(string))}
}

// network
request.InternetAccessible = &cvm.InternetAccessible{}
if v, ok := d.GetOk("internet_charge_type"); ok {
request.InternetAccessible.InternetChargeType = helper.String(v.(string))
}
if v, ok := d.GetOk("internet_max_bandwidth_out"); ok {
maxBandwidthOut := int64(v.(int))
request.InternetAccessible.InternetMaxBandwidthOut = &maxBandwidthOut
}
if v, ok := d.GetOk("bandwidth_package_id"); ok {
request.InternetAccessible.BandwidthPackageId = helper.String(v.(string))
}
if v, ok := d.GetOkExists("allocate_public_ip"); ok {
allocatePublicIp := v.(bool)
request.InternetAccessible.PublicIpAssigned = &allocatePublicIp
}

// vpc
if v, ok := d.GetOk("vpc_id"); ok {
request.VirtualPrivateCloud = &cvm.VirtualPrivateCloud{}
request.VirtualPrivateCloud.VpcId = helper.String(v.(string))

if v, ok = d.GetOk("subnet_id"); ok {
request.VirtualPrivateCloud.SubnetId = helper.String(v.(string))
}
}

if v, ok := d.GetOk("security_groups"); ok {
securityGroups := v.(*schema.Set).List()
request.SecurityGroupIds = make([]*string, 0, len(securityGroups))
for _, securityGroup := range securityGroups {
request.SecurityGroupIds = append(request.SecurityGroupIds, helper.String(securityGroup.(string)))
}
}

// storage
request.SystemDisk = &cvm.SystemDisk{}
if v, ok := d.GetOk("system_disk_type"); ok {
request.SystemDisk.DiskType = helper.String(v.(string))
}
if v, ok := d.GetOk("system_disk_size"); ok {
diskSize := int64(v.(int))
request.SystemDisk.DiskSize = &diskSize
}

// enhanced service
request.EnhancedService = &cvm.EnhancedService{}
if v, ok := d.GetOkExists("disable_security_service"); ok {
securityService := !(v.(bool))
request.EnhancedService.SecurityService = &cvm.RunSecurityServiceEnabled{
Enabled: &securityService,
}
}
if v, ok := d.GetOkExists("disable_monitor_service"); ok {
monitorService := !(v.(bool))
request.EnhancedService.MonitorService = &cvm.RunMonitorServiceEnabled{
Enabled: &monitorService,
}
}

// login
request.LoginSettings = &cvm.LoginSettings{}
if v, ok := d.GetOk("key_name"); ok {
request.LoginSettings.KeyIds = []*string{helper.String(v.(string))}
}
if v, ok := d.GetOk("password"); ok {
request.LoginSettings.Password = helper.String(v.(string))
}
v := d.Get("keep_image_login").(bool)
if v {
request.LoginSettings.KeepImageLogin = helper.String(CVM_IMAGE_LOGIN)
} else {
request.LoginSettings.KeepImageLogin = helper.String(CVM_IMAGE_LOGIN_NOT)
}

if v, ok := d.GetOk("user_data"); ok {
request.UserData = helper.String(v.(string))
}
if v, ok := d.GetOk("user_data_raw"); ok {
userData := base64.StdEncoding.EncodeToString([]byte(v.(string)))
request.UserData = &userData
}

err := resource.Retry(writeRetryTimeout, func() *resource.RetryError {
ratelimit.Check("create")
response, err := meta.(*TencentCloudClient).apiV3Conn.UseCvmClient().RunInstances(request)
if err != nil {
log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n",
logId, request.GetAction(), request.ToJsonString(), err.Error())
e, ok := err.(*sdkErrors.TencentCloudSDKError)
if ok && IsContains(CVM_RETRYABLE_ERROR, e.Code) {
time.Sleep(1 * time.Second) // 需要重试的话,等待1s进行重试
return resource.RetryableError(fmt.Errorf("cvm create error: %s, retrying", e.Error()))
}
return resource.NonRetryableError(err)
}
log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n",
logId, request.GetAction(), request.ToJsonString(), response.ToJsonString())
if len(response.Response.InstanceIdSet) < instanceCount {
err = fmt.Errorf("number of instances is less than %s", strconv.Itoa(instanceCount))
return resource.NonRetryableError(err)
}
createInstanceIds = response.Response.InstanceIdSet

return nil
})
if err != nil {
return err
}

}

var instanceSetIds []*string
if v, ok := d.GetOk("instance_ids"); ok {
instanceSetIds = helper.InterfacesStringsPoint(v.([]interface{}))
}

var newInstanceSetIds []*string
for _, v := range instanceSetIds {
ins := v
noEqual := true
for _, u := range needExclude {
if *ins == u.(string) {
noEqual = false
}
}
if noEqual {
newInstanceSetIds = append(newInstanceSetIds, ins)
}
}

if len(needCreate) > 0 {
for _, v := range createInstanceIds {
ins := v
newInstanceSetIds = append(newInstanceSetIds, ins)
}
}
_ = d.Set("instance_ids", newInstanceSetIds)

}

return nil
}

func doResourceTencentCloudInstanceSetDelete(d *schema.ResourceData, meta interface{}) error {
defer logElapsed("resource.tencentcloud_instance_set.delete")()

logId := getLogId(contextNil)
ctx := context.WithValue(context.TODO(), logIdKey, logId)

instanceSetIds := d.Id()
//instanceSetIds := d.Id()

cvmService := CvmService{
client: meta.(*TencentCloudClient).apiV3Conn,
}

var instanceSetIds []*string
if v, ok := d.GetOk("instance_ids"); ok {
instanceSetIds = helper.InterfacesStringsPoint(v.([]interface{}))
}

// delete
err := resource.Retry(writeRetryTimeout, func() *resource.RetryError {
errRet := cvmService.DeleteInstanceSetByIds(ctx, instanceSetIds)
errRet := cvmService.DeleteInstanceSetByIds(ctx, helper.StrListToStr(instanceSetIds))
if errRet != nil {
log.Printf("[CRITAL][first delete]%s api[%s] fail, reason[%s]\n",
logId, "delete", errRet.Error())
Expand Down
3 changes: 2 additions & 1 deletion website/docs/r/instance_set.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ The following arguments are supported:
* `cam_role_name` - (Optional, ForceNew) CAM role name authorized to access.
* `disable_monitor_service` - (Optional) Disable enhance service for monitor, it is enabled by default. When this options is set, monitor agent won't be installed. Modifying will cause the instance reset.
* `disable_security_service` - (Optional) Disable enhance service for security, it is enabled by default. When this options is set, security agent won't be installed. Modifying will cause the instance reset.
* `exclude_instance_ids` - (Optional) instance ids list to exclude.
* `hostname` - (Optional) The hostname of the instance. Windows instance: The name should be a combination of 2 to 15 characters comprised of letters (case insensitive), numbers, and hyphens (-). Period (.) is not supported, and the name cannot be a string of pure numbers. Other types (such as Linux) of instances: The name should be a combination of 2 to 60 characters, supporting multiple periods (.). The piece between two periods is composed of letters (case insensitive), numbers, and hyphens (-). Modifying will cause the instance reset.
* `instance_charge_type` - (Optional) The charge type of instance. Only support `POSTPAID_BY_HOUR`.
* `instance_count` - (Optional, ForceNew) The number of instances to be purchased. Value range:[1,100]; default value: 1.
* `instance_count` - (Optional) The number of instances to be purchased. Value range:[1,100]; default value: 1.
* `instance_name` - (Optional) The name of the instance. The max length of instance_name is 60, and default value is `Terraform-CVM-Instance`.
* `instance_type` - (Optional) The type of the instance.
* `internet_charge_type` - (Optional, ForceNew) Internet charge type of the instance, Valid values are `BANDWIDTH_PREPAID`, `TRAFFIC_POSTPAID_BY_HOUR`, `BANDWIDTH_POSTPAID_BY_HOUR` and `BANDWIDTH_PACKAGE`. This value does not need to be set when `allocate_public_ip` is false.
Expand Down