Skip to content

Commit dbd9fab

Browse files
authored
Merge pull request #706 from tencentcloudstack/fix/cvm-reboot
enhancement: cvm - support instance reset and pay as you go
2 parents aee5819 + f1d1b5d commit dbd9fab

File tree

4 files changed

+211
-97
lines changed

4 files changed

+211
-97
lines changed

tencentcloud/extension_cvm.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const (
5151

5252
CVM_ZONE_NOT_SUPPORT_ERROR = "InvalidParameterValue.ZoneNotSupported"
5353
CVM_CLOUD_DISK_SOLD_OUT_ERROR = "ResourceInsufficient.CloudDiskSoldOut"
54+
55+
CVM_STOP_MODE_KEEP_CHARGING = "KEEP_CHARGING"
56+
CVM_STOP_MODE_STOP_CHARGING = "STOP_CHARGING"
5457
)
5558

5659
var CVM_CHARGE_TYPE = []string{

tencentcloud/resource_tc_instance.go

Lines changed: 178 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ resource "tencentcloud_cdh_instance" "foo" {
7878
host_type = "HM50"
7979
charge_type = "PREPAID"
8080
instance_charge_type_prepaid_period = 1
81-
host_name = "test"
81+
hostname = "test"
8282
prepaid_renew_flag = "DISABLE_NOTIFY_AND_MANUAL_RENEW"
8383
}
8484
8585
data "tencentcloud_cdh_instances" "list" {
8686
availability_zone = var.availability_zone
8787
host_id = tencentcloud_cdh_instance.foo.id
88-
host_name = "test"
88+
hostname = "test"
8989
host_state = "RUNNING"
9090
}
9191
@@ -167,8 +167,7 @@ func resourceTencentCloudInstance() *schema.Resource {
167167
"image_id": {
168168
Type: schema.TypeString,
169169
Required: true,
170-
ForceNew: true,
171-
Description: "The image to use for the instance. Changing `image_id` will cause the instance to be destroyed and re-created.",
170+
Description: "The image to use for the instance. Changing `image_id` will cause the instance reset.",
172171
},
173172
"availability_zone": {
174173
Type: schema.TypeString,
@@ -180,6 +179,7 @@ func resourceTencentCloudInstance() *schema.Resource {
180179
Type: schema.TypeInt,
181180
Optional: true,
182181
Default: 1,
182+
Deprecated: "It has been deprecated from version 1.59.17. Use built-in `count` instead.",
183183
ValidateFunc: validateIntegerInRange(1, 100),
184184
Description: "The number of instances to be purchased. Value range:[1,100]; default value: 1.",
185185
},
@@ -200,8 +200,7 @@ func resourceTencentCloudInstance() *schema.Resource {
200200
"hostname": {
201201
Type: schema.TypeString,
202202
Optional: true,
203-
ForceNew: true,
204-
Description: "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 (-).",
203+
Description: "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.",
205204
},
206205
"project_id": {
207206
Type: schema.TypeInt,
@@ -215,6 +214,15 @@ func resourceTencentCloudInstance() *schema.Resource {
215214
Default: true,
216215
Description: "Set instance to running or stop. Default value is true, the instance will shutdown when this flag is false.",
217216
},
217+
"stopped_mode": {
218+
Type: schema.TypeString,
219+
Optional: true,
220+
Description: "Billing method of a pay-as-you-go instance after shutdown. Available values: `KEEP_CHARGING`,`STOP_CHARGING`. Default `KEEP_CHARGING`.",
221+
ValidateFunc: validateAllowedStringValue([]string{
222+
CVM_STOP_MODE_KEEP_CHARGING,
223+
CVM_STOP_MODE_STOP_CHARGING,
224+
}),
225+
},
218226
"placement_group_id": {
219227
Type: schema.TypeString,
220228
Optional: true,
@@ -234,14 +242,14 @@ func resourceTencentCloudInstance() *schema.Resource {
234242
Type: schema.TypeInt,
235243
Optional: true,
236244
ValidateFunc: validateAllowedIntValue(CVM_PREPAID_PERIOD),
237-
Description: "The tenancy (time unit is month) of the prepaid instance, NOTE: it only works when instance_charge_type is set to `PREPAID`. Valid values are `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `24`, `36`.",
245+
Description: "The tenancy (time unit is month) of the prepaid instance, NOTE: it only works when instance_charge_type is set to `PREPAID`. Valid values are `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `24`, `36`. Modifying will cause the instance reset.",
238246
},
239247
"instance_charge_type_prepaid_renew_flag": {
240248
Type: schema.TypeString,
241249
Optional: true,
242250
Computed: true,
243251
ValidateFunc: validateAllowedStringValue(CVM_PREPAID_RENEW_FLAG),
244-
Description: "Auto renewal flag. Valid values: `NOTIFY_AND_AUTO_RENEW`: notify upon expiration and renew automatically, `NOTIFY_AND_MANUAL_RENEW`: notify upon expiration but do not renew automatically, `DISABLE_NOTIFY_AND_MANUAL_RENEW`: neither notify upon expiration nor renew automatically. Default value: `NOTIFY_AND_MANUAL_RENEW`. If this parameter is specified as `NOTIFY_AND_AUTO_RENEW`, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to `PREPAID`.",
252+
Description: "Auto renewal flag. Valid values: `NOTIFY_AND_AUTO_RENEW`: notify upon expiration and renew automatically, `NOTIFY_AND_MANUAL_RENEW`: notify upon expiration but do not renew automatically, `DISABLE_NOTIFY_AND_MANUAL_RENEW`: neither notify upon expiration nor renew automatically. Default value: `NOTIFY_AND_MANUAL_RENEW`. If this parameter is specified as `NOTIFY_AND_AUTO_RENEW`, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to `PREPAID`. Modifying will cause the instance reset.",
245253
},
246254
"spot_instance_type": {
247255
Type: schema.TypeString,
@@ -439,9 +447,8 @@ func resourceTencentCloudInstance() *schema.Resource {
439447
return old == new
440448
}
441449
},
442-
ForceNew: true,
443450
ConflictsWith: []string{"key_name", "password"},
444-
Description: "Whether to keep image login or not, default is `false`. When the image type is private or shared or imported, this parameter can be set `true`.",
451+
Description: "Whether to keep image login or not, default is `false`. When the image type is private or shared or imported, this parameter can be set `true`. Modifying will cause the instance reset.",
445452
},
446453
"user_data": {
447454
Type: schema.TypeString,
@@ -797,7 +804,8 @@ func resourceTencentCloudInstanceCreate(d *schema.ResourceData, meta interface{}
797804
}
798805

799806
if !(d.Get("running_flag").(bool)) {
800-
err = cvmService.StopInstance(ctx, instanceId)
807+
stoppedMode := d.Get("stopped_mode").(string)
808+
err = cvmService.StopInstance(ctx, instanceId, stoppedMode)
801809
if err != nil {
802810
return err
803811
}
@@ -838,9 +846,6 @@ func resourceTencentCloudInstanceRead(d *schema.ResourceData, meta interface{})
838846
cvmService := CvmService{
839847
client: client,
840848
}
841-
tkeService := TkeService{
842-
client: client,
843-
}
844849
var instance *cvm.Instance
845850
var errRet error
846851
err := resource.Retry(readRetryTimeout, func() *resource.RetryError {
@@ -858,9 +863,17 @@ func resourceTencentCloudInstanceRead(d *schema.ResourceData, meta interface{})
858863
return nil
859864
}
860865

861-
var tkeImages []string
866+
var cvmImages []string
867+
var response *cvm.DescribeImagesResponse
862868
err = resource.Retry(readRetryTimeout, func() *resource.RetryError {
863-
tkeImages, errRet = tkeService.DescribeImages(ctx)
869+
request := cvm.NewDescribeImagesRequest()
870+
response, errRet = client.UseCvmClient().DescribeImages(request)
871+
if *response.Response.TotalCount > 0 {
872+
for i := range response.Response.ImageSet {
873+
image := response.Response.ImageSet[i]
874+
cvmImages = append(cvmImages, *image.ImageId)
875+
}
876+
}
864877
if errRet != nil {
865878
return retryError(errRet, InternalError)
866879
}
@@ -871,7 +884,7 @@ func resourceTencentCloudInstanceRead(d *schema.ResourceData, meta interface{})
871884
return err
872885
}
873886

874-
if d.Get("image_id").(string) == "" || !IsContains(tkeImages, *instance.ImageId) {
887+
if d.Get("image_id").(string) == "" || !IsContains(cvmImages, *instance.ImageId) {
875888
_ = d.Set("image_id", instance.ImageId)
876889
}
877890

@@ -1038,6 +1051,152 @@ func resourceTencentCloudInstanceUpdate(d *schema.ResourceData, meta interface{}
10381051
d.SetPartial("project_id")
10391052
}
10401053

1054+
// Reset Instance
1055+
// Keep Login Info
1056+
if d.HasChange("image_id") ||
1057+
d.HasChange("host_name") ||
1058+
d.HasChange("disable_security_service") ||
1059+
d.HasChange("disable_monitor_service") ||
1060+
d.HasChange("keep_image_login") {
1061+
1062+
var updateAttr []string
1063+
1064+
request := cvm.NewResetInstanceRequest()
1065+
request.InstanceId = helper.String(d.Id())
1066+
1067+
if v, ok := d.GetOk("image_id"); ok {
1068+
updateAttr = append(updateAttr, "image_id")
1069+
request.ImageId = helper.String(v.(string))
1070+
}
1071+
if v, ok := d.GetOk("hostname"); ok {
1072+
updateAttr = append(updateAttr, "hostname")
1073+
request.HostName = helper.String(v.(string))
1074+
}
1075+
1076+
// enhanced service
1077+
request.EnhancedService = &cvm.EnhancedService{}
1078+
if d.HasChange("disable_security_service") {
1079+
updateAttr = append(updateAttr, "disable_security_service")
1080+
v := d.Get("disable_security_service")
1081+
securityService := v.(bool)
1082+
request.EnhancedService.SecurityService = &cvm.RunSecurityServiceEnabled{
1083+
Enabled: &securityService,
1084+
}
1085+
}
1086+
1087+
if d.HasChange("disable_monitor_service") {
1088+
updateAttr = append(updateAttr, "disable_monitor_service")
1089+
v := d.Get("disable_monitor_service")
1090+
monitorService := !(v.(bool))
1091+
request.EnhancedService.MonitorService = &cvm.RunMonitorServiceEnabled{
1092+
Enabled: &monitorService,
1093+
}
1094+
}
1095+
1096+
// Modify or keep login info when instance reset
1097+
request.LoginSettings = &cvm.LoginSettings{}
1098+
1099+
if v, ok := d.GetOk("password"); ok {
1100+
updateAttr = append(updateAttr, "password")
1101+
request.LoginSettings.Password = helper.String(v.(string))
1102+
}
1103+
1104+
if v, ok := d.GetOk("key_name"); ok {
1105+
updateAttr = append(updateAttr, "key_name")
1106+
request.LoginSettings.KeyIds = []*string{helper.String(v.(string))}
1107+
}
1108+
1109+
if d.HasChange("keep_image_login") {
1110+
updateAttr = append(updateAttr, "keep_image_login")
1111+
}
1112+
1113+
if v := d.Get("keep_image_login").(bool); v {
1114+
request.LoginSettings.KeepImageLogin = helper.String(CVM_IMAGE_LOGIN)
1115+
} else {
1116+
request.LoginSettings.KeepImageLogin = helper.String(CVM_IMAGE_LOGIN_NOT)
1117+
}
1118+
1119+
if err := cvmService.ResetInstance(ctx, request); err != nil {
1120+
return err
1121+
}
1122+
1123+
for _, attr := range updateAttr {
1124+
d.SetPartial(attr)
1125+
}
1126+
1127+
// Modify Login Info Directly
1128+
} else {
1129+
if d.HasChange("password") {
1130+
err := cvmService.ModifyPassword(ctx, instanceId, d.Get("password").(string))
1131+
if err != nil {
1132+
return err
1133+
}
1134+
d.SetPartial("password")
1135+
time.Sleep(10 * time.Second)
1136+
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1137+
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1138+
if errRet != nil {
1139+
return retryError(errRet, InternalError)
1140+
}
1141+
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1142+
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1143+
}
1144+
return nil
1145+
})
1146+
if err != nil {
1147+
return err
1148+
}
1149+
}
1150+
1151+
if d.HasChange("key_name") {
1152+
old, new := d.GetChange("key_name")
1153+
oldKeyId := old.(string)
1154+
keyId := new.(string)
1155+
if oldKeyId != "" {
1156+
err := cvmService.UnbindKeyPair(ctx, oldKeyId, []*string{&instanceId})
1157+
if err != nil {
1158+
return err
1159+
}
1160+
time.Sleep(10 * time.Second)
1161+
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1162+
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1163+
if errRet != nil {
1164+
return retryError(errRet, InternalError)
1165+
}
1166+
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1167+
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1168+
}
1169+
return nil
1170+
})
1171+
if err != nil {
1172+
return err
1173+
}
1174+
}
1175+
1176+
if keyId != "" {
1177+
err = cvmService.BindKeyPair(ctx, keyId, instanceId)
1178+
if err != nil {
1179+
return err
1180+
}
1181+
time.Sleep(10 * time.Second)
1182+
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1183+
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1184+
if errRet != nil {
1185+
return retryError(errRet, InternalError)
1186+
}
1187+
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1188+
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1189+
}
1190+
return nil
1191+
})
1192+
if err != nil {
1193+
return err
1194+
}
1195+
}
1196+
d.SetPartial("key_name")
1197+
}
1198+
}
1199+
10411200
var flag bool
10421201
if d.HasChange("running_flag") {
10431202
flag = d.Get("running_flag").(bool)
@@ -1060,7 +1219,8 @@ func resourceTencentCloudInstanceUpdate(d *schema.ResourceData, meta interface{}
10601219
return err
10611220
}
10621221
} else {
1063-
err = cvmService.StopInstance(ctx, instanceId)
1222+
stoppedMode := d.Get("stopped_mode").(string)
1223+
err = cvmService.StopInstance(ctx, instanceId, stoppedMode)
10641224
if err != nil {
10651225
return err
10661226
}
@@ -1133,76 +1293,6 @@ func resourceTencentCloudInstanceUpdate(d *schema.ResourceData, meta interface{}
11331293
}
11341294
}
11351295

1136-
if d.HasChange("password") {
1137-
err := cvmService.ModifyPassword(ctx, instanceId, d.Get("password").(string))
1138-
if err != nil {
1139-
return err
1140-
}
1141-
d.SetPartial("password")
1142-
time.Sleep(10 * time.Second)
1143-
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1144-
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1145-
if errRet != nil {
1146-
return retryError(errRet, InternalError)
1147-
}
1148-
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1149-
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1150-
}
1151-
return nil
1152-
})
1153-
if err != nil {
1154-
return err
1155-
}
1156-
}
1157-
1158-
if d.HasChange("key_name") {
1159-
old, new := d.GetChange("key_name")
1160-
oldKeyId := old.(string)
1161-
keyId := new.(string)
1162-
if oldKeyId != "" {
1163-
err := cvmService.UnbindKeyPair(ctx, oldKeyId, []*string{&instanceId})
1164-
if err != nil {
1165-
return err
1166-
}
1167-
time.Sleep(10 * time.Second)
1168-
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1169-
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1170-
if errRet != nil {
1171-
return retryError(errRet, InternalError)
1172-
}
1173-
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1174-
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1175-
}
1176-
return nil
1177-
})
1178-
if err != nil {
1179-
return err
1180-
}
1181-
}
1182-
1183-
if keyId != "" {
1184-
err = cvmService.BindKeyPair(ctx, keyId, instanceId)
1185-
if err != nil {
1186-
return err
1187-
}
1188-
time.Sleep(10 * time.Second)
1189-
err = resource.Retry(2*readRetryTimeout, func() *resource.RetryError {
1190-
instance, errRet := cvmService.DescribeInstanceById(ctx, instanceId)
1191-
if errRet != nil {
1192-
return retryError(errRet, InternalError)
1193-
}
1194-
if instance != nil && *instance.LatestOperationState == CVM_LATEST_OPERATION_STATE_OPERATING {
1195-
return resource.RetryableError(fmt.Errorf("cvm instance latest operetion status is %s, retry...", *instance.LatestOperationState))
1196-
}
1197-
return nil
1198-
})
1199-
if err != nil {
1200-
return err
1201-
}
1202-
}
1203-
d.SetPartial("key_name")
1204-
}
1205-
12061296
if d.HasChange("vpc_id") || d.HasChange("subnet_id") || d.HasChange("private_ip") {
12071297
vpcId := d.Get("vpc_id").(string)
12081298
subnetId := d.Get("subnet_id").(string)

0 commit comments

Comments
 (0)