alibabacloudstack/resource_apsarastack_ecs_instance.go (1,195 lines of code) (raw):
package alibabacloudstack
import (
"errors"
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"encoding/base64"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/connectivity"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/errmsgs"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func resourceAlibabacloudStackInstance() *schema.Resource {
resource := &schema.Resource{
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(20 * time.Minute),
Update: schema.DefaultTimeout(20 * time.Minute),
Delete: schema.DefaultTimeout(20 * time.Minute),
},
Schema: map[string]*schema.Schema{
"availability_zone": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Computed: true,
Deprecated: "Field 'availability_zone' is deprecated and will be removed in a future release. Please use new field 'zone_id' instead.",
ConflictsWith: []string{"zone_id"},
},
"zone_id": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Computed: true,
ConflictsWith: []string{"availability_zone"},
},
"image_id": {
Type: schema.TypeString,
Optional: true,
},
"instance_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile(`^ecs\..*`), "prefix must be 'ecs.'"),
},
"security_groups": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
"instance_name": {
Type: schema.TypeString,
Optional: true,
Default: "ECS-Instance",
ValidateFunc: validation.StringLenBetween(2, 128),
},
"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(2, 256),
},
"internet_max_bandwidth_in": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntBetween(1, 200),
},
"internet_max_bandwidth_out": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
},
"host_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"password": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
},
"kms_encrypted_password": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: kmsDiffSuppressFunc,
},
"kms_encryption_context": {
Type: schema.TypeMap,
Optional: true,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return d.Get("kms_encrypted_password") == ""
},
Elem: schema.TypeString,
},
"is_outdated": {
Type: schema.TypeBool,
Optional: true,
},
"system_disk_category": {
Type: schema.TypeString,
Default: DiskCloudEfficiency,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"all", "cloud", "ephemeral_ssd", "cloud_efficiency", "cloud_ssd", "cloud_pperf", "cloud_sperf"}, false),
},
"system_disk_size": {
Type: schema.TypeInt,
Optional: true,
Default: 40,
},
"system_disk_name": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(2, 128),
},
"system_disk_description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(2, 256),
},
"system_disk_id": {
Type: schema.TypeString,
Computed: true,
},
"data_disks": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
MaxItems: 16,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(2, 128),
},
"size": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"category": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"all", "cloud", "ephemeral_ssd", "cloud_efficiency", "cloud_ssd", "cloud_pperf", "cloud_sperf"}, false),
Default: DiskCloudEfficiency,
ForceNew: true,
},
"encrypted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
"kms_key_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"snapshot_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"delete_with_instance": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Default: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(2, 256),
},
},
},
},
"subnet_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"vswitch_id"},
},
"vswitch_id": {
Type: schema.TypeString,
Optional: true,
},
"private_ip": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"hpc_cluster_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"user_data": {
Type: schema.TypeString,
Optional: true,
},
"role_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
DiffSuppressFunc: vpcTypeResourceDiffSuppressFunc,
},
"key_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"storage_set_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"storage_set_partition_number": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(1, 2000),
},
"security_enhancement_strategy": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(ActiveSecurityEnhancementStrategy),
string(DeactiveSecurityEnhancementStrategy),
}, false),
},
"enable_ipv6": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"ipv6_cidr_block": {
Type: schema.TypeString,
Optional: true,
},
"ipv6_address_count": {
Type: schema.TypeInt,
Optional: true,
Default: 0,
ValidateFunc: validation.IntBetween(0, 10),
},
"ipv6_address_list": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"tags": tagsSchema(),
"system_disk_tags": tagsSchema(),
"data_disk_tags": tagsSchema(),
},
}
setResourceFunc(resource, resourceAlibabacloudStackInstanceCreate, resourceAlibabacloudStackInstanceRead, resourceAlibabacloudStackInstanceUpdate, resourceAlibabacloudStackInstanceDelete)
return resource
}
func resourceAlibabacloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
request, err := buildAlibabacloudStackInstanceArgs(d, meta)
if err != nil {
return errmsgs.WrapError(err)
}
request.IoOptimized = string(IOOptimized)
if d.Get("is_outdated").(bool) {
request.IoOptimized = string(NoneOptimized)
}
client.InitRpcRequest(*request.RpcRequest)
wait := incrementalWait(1*time.Second, 1*time.Second)
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.RunInstances(request)
})
if err != nil {
if errmsgs.IsThrottling(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ := raw.(*ecs.RunInstancesResponse)
for _, k := range response.InstanceIdSets.InstanceIdSet {
d.SetId(k)
}
return nil
})
if err != nil {
return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, "alibabacloudstack_instance", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
stateConf := BuildStateConf([]string{"Pending", "Starting", "Stopped"}, []string{"Running"}, d.Timeout(schema.TimeoutCreate), 60*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{"Stopping"}))
if _, err := stateConf.WaitForState(); err != nil {
return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id())
}
if v, ok := d.GetOk("security_groups"); ok {
sgs := expandStringList(v.(*schema.Set).List())
if len(sgs) > 1 {
err := ecsService.JoinSecurityGroups(d.Id(), sgs[1:])
if err != nil {
return errmsgs.WrapError(err)
}
}
}
if d.Get("enable_ipv6").(bool) && d.Get("ipv6_address_count").(int) > 0 {
_, err := AssignIpv6AddressesFunc(d.Id(), d.Get("ipv6_address_count").(int), d.Get("ipv6_address_list").([]string), meta)
if err != nil {
return errmsgs.WrapError(err)
}
}
return nil
}
func resourceAlibabacloudStackInstanceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
if errmsgs.NotFoundError(err) {
d.SetId("")
return nil
}
return errmsgs.WrapError(err)
}
log.Printf("[ECS Creation]: Getting Instance Details Successfully: %s", instance.Status)
system_disks, err := ecsService.DescribeInstanceDisksByType(d.Id(), client.ResourceGroup, "system")
if err != nil {
return errmsgs.WrapError(err)
}
system_disk_tags := getOnlySystemTags(d, system_disks[0].Tags.Tag)
d.Set("system_disk_category", system_disks[0].Category)
d.Set("system_disk_size", system_disks[0].Size)
d.Set("system_disk_name", system_disks[0].DiskName)
d.Set("system_disk_description", system_disks[0].Description)
d.Set("system_disk_id", system_disks[0].DiskId)
d.Set("system_disk_tags", ecsService.tagsToMap(system_disk_tags))
d.Set("instance_name", instance.InstanceName)
d.Set("description", instance.Description)
d.Set("status", instance.Status)
connectivity.SetResourceData(d, instance.ZoneId, "zone_id", "availability_zone")
d.Set("host_name", instance.HostName)
d.Set("image_id", instance.ImageId)
d.Set("instance_type", instance.InstanceType)
d.Set("password", d.Get("password").(string))
d.Set("internet_max_bandwidth_out", instance.InternetMaxBandwidthOut)
d.Set("internet_max_bandwidth_in", instance.InternetMaxBandwidthIn)
d.Set("key_name", instance.KeyPairName)
d.Set("hpc_cluster_id", instance.HpcClusterId)
d.Set("tags", ecsService.tagsToMap(instance.Tags.Tag))
d.Set("vswitch_id", instance.VpcAttributes.VSwitchId)
if len(instance.VpcAttributes.PrivateIpAddress.IpAddress) > 0 {
d.Set("private_ip", instance.VpcAttributes.PrivateIpAddress.IpAddress[0])
} else {
d.Set("private_ip", strings.Join(instance.InnerIpAddress.IpAddress, ","))
}
sgs := make([]string, 0, len(instance.SecurityGroupIds.SecurityGroupId))
for _, sg := range instance.SecurityGroupIds.SecurityGroupId {
sgs = append(sgs, sg)
}
if err := d.Set("security_groups", sgs); err != nil {
return errmsgs.WrapError(err)
}
if !d.IsNewResource() || d.HasChange("user_data") {
dataRequest := ecs.CreateDescribeUserDataRequest()
client.InitRpcRequest(*dataRequest.RpcRequest)
dataRequest.InstanceId = d.Id()
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeUserData(dataRequest)
})
if err != nil {
errmsg := ""
if bresponse, ok := raw.(*ecs.DescribeUserDataResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), dataRequest.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(dataRequest.GetActionName(), raw, dataRequest.RpcRequest, dataRequest)
response, _ := raw.(*ecs.DescribeUserDataResponse)
old_s := base64.StdEncoding.EncodeToString([]byte(response.UserData))
d.Set("user_data", d.Get("user_data").(string))
log.Printf("data : %s", old_s)
}
if len(instance.VpcAttributes.VSwitchId) > 0 && (!d.IsNewResource() || d.HasChange("role_name")) {
request := ecs.CreateDescribeInstanceRamRoleRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceIds = convertListToJsonString([]interface{}{d.Id()})
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeInstanceRamRole(request)
})
if err != nil {
errmsg := ""
if bresponse, ok := raw.(*ecs.DescribeInstanceRamRoleResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ := raw.(*ecs.DescribeInstanceRamRoleResponse)
log.Printf("[ECS Creation]: Getting Instance RamRole Details: %s ", response.InstanceRamRoleSets.InstanceRamRoleSet)
if len(response.InstanceRamRoleSets.InstanceRamRoleSet) >= 1 {
d.Set("role_name", response.InstanceRamRoleSets.InstanceRamRoleSet[0].RamRoleName)
}
}
if instance.InstanceChargeType == string(PrePaid) {
request := ecs.CreateDescribeInstanceAutoRenewAttributeRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeInstanceAutoRenewAttribute(request)
})
if err != nil {
errmsg := ""
if bresponse, ok := raw.(*ecs.DescribeInstanceAutoRenewAttributeResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
// 查询 ipv6地址集
// request := ecs.CreateDescribeNetworkInterfacesRequest()
// client.InitRpcRequest(*request.RpcRequest)
// request.InstanceId = d.Id()
// raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
// return ecsClient.DescribeNetworkInterfaces(request)
// })
// bresponse, ok := raw.(*ecs.DescribeNetworkInterfacesResponse)
// if err != nil {
// errmsg := ""
// if ok {
// errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
// }
// return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
// }
// ipv6_address_list := make([]string, 0)
// if len(bresponse.NetworkInterfaceSets.NetworkInterfaceSet) > 0 && len(bresponse.NetworkInterfaceSets.NetworkInterfaceSet[0].Ipv6Sets.Ipv6Set) > 0 {
// for _, ipv6 := range bresponse.NetworkInterfaceSets.NetworkInterfaceSet[0].Ipv6Sets.Ipv6Set {
// ipv6_address_list = append(ipv6_address_list, ipv6.Ipv6Address)
// }
// d.Set("ipv6_address_list", ipv6_address_list)
// }
return nil
}
func resourceAlibabacloudStackInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
d.Partial(true)
if !d.IsNewResource() {
err := setTags(client, TagResourceInstance, d)
if err != nil {
return errmsgs.WrapError(err)
}
}
if d.HasChange("security_groups") {
if !d.IsNewResource() || d.Get("vswitch_id").(string) == "" {
o, n := d.GetChange("security_groups")
os := o.(*schema.Set)
ns := n.(*schema.Set)
rl := expandStringList(os.Difference(ns).List())
al := expandStringList(ns.Difference(os).List())
if len(al) > 0 {
err := ecsService.JoinSecurityGroups(d.Id(), al)
if err != nil {
return errmsgs.WrapError(err)
}
}
if len(rl) > 0 {
err := ecsService.LeaveSecurityGroups(d.Id(), rl)
if err != nil {
return errmsgs.WrapError(err)
}
}
}
}
run := false
imageUpdate, err := modifyInstanceImage(d, meta, run)
if err != nil {
return errmsgs.WrapError(err)
}
vpcUpdate, err := modifyVpcAttribute(d, meta, run)
if err != nil {
return errmsgs.WrapError(err)
}
passwordUpdate, err := modifyInstanceAttribute(d, meta)
if err != nil {
return errmsgs.WrapError(err)
}
typeUpdate, err := modifyInstanceType(d, meta, run)
if err != nil {
return errmsgs.WrapError(err)
}
if imageUpdate || vpcUpdate || passwordUpdate || typeUpdate {
run = true
instance, errDesc := ecsService.DescribeInstance(d.Id())
if errDesc != nil {
return errmsgs.WrapError(errDesc)
}
if instance.Status == string(Running) {
stopRequest := ecs.CreateStopInstanceRequest()
client.InitRpcRequest(*stopRequest.RpcRequest)
stopRequest.InstanceId = d.Id()
stopRequest.ForceStop = requests.NewBoolean(false)
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.StopInstance(stopRequest)
})
if err != nil {
errmsg := ""
if bresponse, ok := raw.(*ecs.StopInstanceResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), stopRequest.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(stopRequest.GetActionName(), raw)
}
stateConf := BuildStateConf([]string{"Pending", "Running", "Stopping"}, []string{"Stopped"}, d.Timeout(schema.TimeoutUpdate), 5*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{}))
if _, err = stateConf.WaitForState(); err != nil {
return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id())
}
if _, err := modifyInstanceImage(d, meta, run); err != nil {
return errmsgs.WrapError(err)
}
if _, err := modifyVpcAttribute(d, meta, run); err != nil {
return errmsgs.WrapError(err)
}
if _, err := modifyInstanceType(d, meta, run); err != nil {
return errmsgs.WrapError(err)
}
startRequest := ecs.CreateStartInstanceRequest()
client.InitRpcRequest(*startRequest.RpcRequest)
startRequest.InstanceId = d.Id()
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.StartInstance(startRequest)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"IncorrectInstanceStatus"}) {
time.Sleep(time.Second)
return resource.RetryableError(err)
}
errmsg := ""
if bresponse, ok := raw.(*ecs.StartInstanceResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
err = errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), startRequest.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
return resource.NonRetryableError(err)
}
addDebug(startRequest.GetActionName(), raw)
return nil
})
if err != nil {
return err
}
stateConf = &resource.StateChangeConf{
Pending: []string{"Pending", "Starting", "Stopped"},
Target: []string{"Running"},
Refresh: ecsService.InstanceStateRefreshFunc(d.Id(), []string{}),
Timeout: d.Timeout(schema.TimeoutUpdate),
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}
if _, err = stateConf.WaitForState(); err != nil {
return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id())
}
}
if err := modifyInstanceNetworkSpec(d, meta); err != nil {
return errmsgs.WrapError(err)
}
if d.HasChanges("system_disk_tags", "system_disk_id", "image_id", "tags") {
var oraw, nraw map[string]interface{}
system_disks, err := ecsService.DescribeInstanceDisksByType(d.Id(), client.ResourceGroup, "system")
if err != nil {
return errmsgs.WrapError(err)
}
system_disk_tags := getOnlySystemTags(d, system_disks[0].Tags.Tag)
oraw = make(map[string]interface{})
for k, v := range ecsService.tagsToMap(system_disk_tags) {
oraw[k] = v
}
if v, ok := d.GetOk("system_disk_tags"); ok {
nraw = v.(map[string]interface{})
}
err = updateTags(client, []string{system_disks[0].DiskId}, "disk", oraw, nraw)
if err != nil {
return errmsgs.WrapError(err)
}
}
if data_disk_tags, ok := d.GetOk("data_disk_tags"); ok {
disks, err := ecsService.DescribeInstanceDisksByType(d.Id(), client.ResourceGroup, "data")
if err != nil {
return errmsgs.WrapError(err)
}
if len(disks) > 0 {
oraw := make(map[string]interface{})
diskids := make([]string, 0, len(disks))
datadisk_tags := ecsMergeTags(d, data_disk_tags.(map[string]interface{}))
for _, disk := range disks {
diskids = append(diskids, disk.DiskId)
}
err = updateTags(client, diskids, "disk", oraw, datadisk_tags)
if err != nil {
return errmsgs.WrapError(err)
}
}
}
d.Partial(false)
return nil
}
func resourceAlibabacloudStackInstanceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
stopRequest := ecs.CreateStopInstanceRequest()
client.InitRpcRequest(*stopRequest.RpcRequest)
stopRequest.InstanceId = d.Id()
stopRequest.ForceStop = requests.NewBoolean(true)
deleteRequest := ecs.CreateDeleteInstanceRequest()
client.InitRpcRequest(*deleteRequest.RpcRequest)
deleteRequest.InstanceId = d.Id()
deleteRequest.Force = requests.NewBoolean(true)
wait := incrementalWait(1*time.Second, 1*time.Second)
err := resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DeleteInstance(deleteRequest)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"IncorrectInstanceStatus", "DependencyViolation.RouteEntry", "IncorrectInstanceStatus.Initializing"}) {
return resource.RetryableError(err)
}
if errmsgs.IsExpectedErrors(err, []string{errmsgs.Throttling, "LastTokenProcessing"}) {
wait()
return resource.RetryableError(err)
}
errmsg := ""
if bresponse, ok := raw.(*ecs.StartInstanceResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
err = errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), "StartInstance", errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
return resource.NonRetryableError(err)
}
addDebug(deleteRequest.GetActionName(), raw)
return nil
})
if err != nil {
if errmsgs.IsExpectedErrors(err, errmsgs.EcsNotFound) {
return nil
}
return err
}
stateConf := BuildStateConf([]string{"Pending", "Running", "Stopped", "Stopping"}, []string{}, d.Timeout(schema.TimeoutDelete), 10*time.Second, ecsService.InstanceStateRefreshFunc(d.Id(), []string{}))
if _, err = stateConf.WaitForState(); err != nil {
return errmsgs.WrapErrorf(err, errmsgs.IdMsg, d.Id())
}
return nil
}
func buildAlibabacloudStackInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.RunInstancesRequest, error) {
client := meta.(*connectivity.AlibabacloudStackClient)
request := ecs.CreateRunInstancesRequest()
client.InitRpcRequest(*request.RpcRequest)
if v := d.Get("enable_ipv6").(bool); v {
request.QueryParams["Ipv6CidrBlock"] = d.Get("ipv6_cidr_block").(string)
if d.Get("ipv6_address_count").(int) <= 0 {
return nil, errmsgs.WrapError(errmsgs.Error("if enable_ipv6 = true, ipv6_address_count must be greater than 0"))
}
request.QueryParams["Ipv6AddressCount"] = "0"
}
request.InstanceType = d.Get("instance_type").(string)
request.SystemDiskDiskName = d.Get("system_disk_name").(string)
imageID := d.Get("image_id").(string)
request.ImageId = imageID
if v := d.Get("system_disk_description").(string); v != "" {
request.SystemDiskDescription = v
}
systemDiskCategory := DiskCategory(d.Get("system_disk_category").(string))
if v, ok := connectivity.GetResourceDataOk(d, "zone_id", "availability_zone"); ok && v.(string) != "" {
request.ZoneId = v.(string)
}
request.SystemDiskCategory = string(systemDiskCategory)
request.SystemDiskSize = strconv.Itoa(d.Get("system_disk_size").(int))
if v, ok := d.GetOk("security_groups"); ok {
sgs := expandStringList(v.(*schema.Set).List())
request.SecurityGroupId = sgs[0]
}
if v := d.Get("instance_name").(string); v != "" {
request.InstanceName = v
}
if v := d.Get("description").(string); v != "" {
request.Description = v
}
if v, ok := d.GetOk("internet_max_bandwidth_in"); ok {
request.InternetMaxBandwidthIn = requests.NewInteger(v.(int))
}
if v := d.Get("host_name").(string); v != "" {
request.HostName = v
}
if v := d.Get("password").(string); v != "" {
request.Password = v
}
if v := d.Get("kms_encrypted_password").(string); v != "" {
kmsService := KmsService{client}
decryptResp, err := kmsService.Decrypt(v, d.Get("kms_encryption_context").(map[string]interface{}))
if err != nil {
return request, errmsgs.WrapError(err)
}
request.Password = decryptResp.Plaintext
}
vswitchValue := d.Get("subnet_id").(string)
if vswitchValue == "" {
vswitchValue = d.Get("vswitch_id").(string)
}
if vswitchValue != "" {
request.VSwitchId = vswitchValue
if v, ok := d.GetOk("private_ip"); ok && v.(string) != "" {
request.PrivateIpAddress = v.(string)
}
}
if v := d.Get("user_data").(string); v != "" {
_, base64DecodeError := base64.StdEncoding.DecodeString(v)
if base64DecodeError == nil {
request.UserData = v
} else {
request.UserData = base64.StdEncoding.EncodeToString([]byte(v))
}
}
if v := d.Get("role_name").(string); v != "" {
request.RamRoleName = v
}
if v := d.Get("key_name").(string); v != "" {
request.KeyPairName = v
}
request.InternetMaxBandwidthOut = requests.NewInteger(d.Get("internet_max_bandwidth_out").(int))
i := d.Get("storage_set_partition_number").(int)
if v := d.Get("storage_set_id").(string); v != "" {
request.StorageSetId = v
if i >= 1 {
request.StorageSetPartitionNumber = requests.NewInteger(d.Get("storage_set_partition_number").(int))
} else {
return nil, fmt.Errorf("can't empty storage_set_partition_number when you set storage_set_id and >=2 ")
}
}
if v, ok := d.GetOk("security_enhancement_strategy"); ok {
request.SecurityEnhancementStrategy = v.(string)
}
v, ok := d.GetOk("tags")
if ok && len(v.(map[string]interface{})) > 0 {
tags := make([]ecs.RunInstancesTag, 0)
for key, value := range v.(map[string]interface{}) {
tags = append(tags, ecs.RunInstancesTag{
Key: key,
Value: value.(string),
})
}
request.Tag = &tags
}
request.ClientToken = buildClientToken(request.GetActionName())
if v, ok := d.GetOk("data_disks"); ok {
disks := v.([]interface{})
var dataDiskRequests []ecs.RunInstancesDataDisk
for i := range disks {
disk := disks[i].(map[string]interface{})
dataDiskRequest := ecs.RunInstancesDataDisk{
Category: disk["category"].(string),
DeleteWithInstance: strconv.FormatBool(disk["delete_with_instance"].(bool)),
Encrypted: strconv.FormatBool(disk["encrypted"].(bool)),
}
if enc, ok := disk["encrypted"]; ok {
if enc.(bool) {
if j, ok := disk["kms_key_id"]; ok {
dataDiskRequest.KMSKeyId = j.(string)
}
if dataDiskRequest.KMSKeyId == "" {
return nil, errmsgs.WrapError(errors.New("KmsKeyId can not be empty if encrypted is set to \"true\""))
}
}
}
if kms, ok := disk["kms_key_id"]; ok {
dataDiskRequest.KMSKeyId = kms.(string)
}
if name, ok := disk["name"]; ok {
dataDiskRequest.DiskName = name.(string)
}
if snapshotId, ok := disk["snapshot_id"]; ok {
dataDiskRequest.SnapshotId = snapshotId.(string)
}
if description, ok := disk["description"]; ok {
dataDiskRequest.Description = description.(string)
}
dataDiskRequest.Size = fmt.Sprintf("%d", disk["size"].(int))
dataDiskRequest.Category = disk["category"].(string)
if dataDiskRequest.Category == string(DiskEphemeralSSD) {
dataDiskRequest.DeleteWithInstance = ""
}
dataDiskRequests = append(dataDiskRequests, dataDiskRequest)
}
request.DataDisk = &dataDiskRequests
}
return request, nil
}
func modifyInstanceImage(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
update := false
if d.HasChanges("image_id", "system_disk_size") {
update = true
if !run {
return update, nil
}
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return update, errmsgs.WrapError(err)
}
keyPairName := instance.KeyPairName
request := ecs.CreateReplaceSystemDiskRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
request.ImageId = d.Get("image_id").(string)
request.SystemDiskSize = requests.NewInteger(d.Get("system_disk_size").(int))
request.ClientToken = buildClientToken(request.GetActionName())
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ReplaceSystemDisk(request)
})
if err != nil {
errmsg := ""
if bresponse, ok := raw.(*ecs.ReplaceSystemDiskResponse); ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(bresponse.BaseResponse)
}
return update, errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
// Ensure instance's image has been replaced successfully.
timeout := DefaultTimeoutMedium
for {
instance, errDesc := ecsService.DescribeInstance(d.Id())
if errDesc != nil {
return update, errmsgs.WrapError(errDesc)
}
var disks []ecs.Disk
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
disks, err = ecsService.DescribeInstanceDisksByType(d.Id(), client.ResourceGroup, "system")
if err != nil {
if errmsgs.NotFoundError(err) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return update, errmsgs.WrapError(err)
}
if instance.ImageId == d.Get("image_id") && disks[0].Size == d.Get("system_disk_size").(int) {
break
}
time.Sleep(DefaultIntervalShort * time.Second)
timeout = timeout - DefaultIntervalShort
if timeout <= 0 {
return update, errmsgs.WrapError(errmsgs.GetTimeErrorFromString(fmt.Sprintf("Replacing instance %s system disk timeout.", d.Id())))
}
}
// After updating image, it need to re-attach key pair
if keyPairName != "" {
if err := ecsService.AttachKeyPair(keyPairName, []interface{}{d.Id()}); err != nil {
return update, errmsgs.WrapError(err)
}
}
}
return update, nil
}
func modifyInstanceAttribute(d *schema.ResourceData, meta interface{}) (bool, error) {
client := meta.(*connectivity.AlibabacloudStackClient)
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
update := false
reboot := false
request := ecs.CreateModifyInstanceAttributeRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
if d.HasChange("instance_name") {
//d.SetPartial("instance_name")
request.InstanceName = d.Get("instance_name").(string)
update = true
}
if d.HasChange("description") {
//d.SetPartial("description")
request.Description = d.Get("description").(string)
update = true
}
if d.HasChange("user_data") {
//d.SetPartial("user_data")
old, new := d.GetChange("user_data")
old_s := base64.StdEncoding.EncodeToString([]byte(fmt.Sprint(old)))
if fmt.Sprint(new) != old_s {
if v, ok := d.GetOk("user_data"); ok && v.(string) != "" {
_, base64DecodeError := base64.StdEncoding.DecodeString(v.(string))
if base64DecodeError == nil {
request.UserData = v.(string)
} else {
request.UserData = base64.StdEncoding.EncodeToString([]byte(v.(string)))
}
}
update = true
reboot = true
}
}
if d.HasChange("host_name") {
//d.SetPartial("host_name")
request.HostName = d.Get("host_name").(string)
update = true
reboot = true
}
if d.HasChanges("password", "kms_encrypted_password") {
if v := d.Get("password").(string); v != "" {
//d.SetPartial("password")
request.Password = v
update = true
reboot = true
}
if v := d.Get("kms_encrypted_password").(string); v != "" {
kmsService := KmsService{meta.(*connectivity.AlibabacloudStackClient)}
decryptResp, err := kmsService.Decrypt(v, d.Get("kms_encryption_context").(map[string]interface{}))
if err != nil {
return reboot, errmsgs.WrapError(err)
}
request.Password = decryptResp.Plaintext
//d.SetPartial("kms_encrypted_password")
//d.SetPartial("kms_encryption_context")
update = true
reboot = true
}
}
if update {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceAttribute(request)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"InvalidChargeType.ValueNotSupported"}) {
time.Sleep(time.Minute)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return reboot, errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
}
return reboot, nil
}
func modifyVpcAttribute(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
client := meta.(*connectivity.AlibabacloudStackClient)
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
update := false
request := ecs.CreateModifyInstanceVpcAttributeRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
request.VSwitchId = d.Get("vswitch_id").(string)
if d.HasChange("vswitch_id") {
update = true
if d.Get("vswitch_id").(string) == "" {
return update, errmsgs.WrapError(errmsgs.Error("Field 'vswitch_id' is required when modifying the instance VPC attribute."))
}
//d.SetPartial("vswitch_id")
}
if d.HasChange("subnet_id") {
update = true
if d.Get("subnet_id").(string) == "" {
return update, errmsgs.WrapError(errmsgs.Error("Field 'subnet_id' is required when modifying the instance VPC attribute."))
}
request.VSwitchId = d.Get("subnet_id").(string)
//d.SetPartial("subnet_id")
}
if request.VSwitchId != "" && d.HasChange("private_ip") {
request.PrivateIpAddress = d.Get("private_ip").(string)
update = true
//d.SetPartial("private_ip")
}
if !run {
return update, nil
}
if update {
client := meta.(*connectivity.AlibabacloudStackClient)
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceVpcAttribute(request)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"OperationConflict"}) {
time.Sleep(1 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return update, errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
ecsService := EcsService{client}
if err := ecsService.WaitForVpcAttributesChanged(d.Id(), request.VSwitchId, request.PrivateIpAddress); err != nil {
return update, errmsgs.WrapError(err)
}
}
return update, nil
}
func modifyInstanceType(d *schema.ResourceData, meta interface{}, run bool) (bool, error) {
if d.IsNewResource() {
d.Partial(false)
return false, nil
}
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client}
update := false
if d.HasChange("instance_type") {
update = true
if !run {
return update, nil
}
//An instance that was successfully modified once cannot be modified again within 5 minutes.
request := ecs.CreateModifyInstanceSpecRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
request.InstanceType = d.Get("instance_type").(string)
request.ClientToken = buildClientToken(request.GetActionName())
err := resource.Retry(6*time.Minute, func() *resource.RetryError {
args := *request
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceSpec(&args)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{errmsgs.Throttling}) {
time.Sleep(10 * time.Second)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return update, errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
// Ensure instance's type has been replaced successfully.
timeout := DefaultTimeoutMedium
for {
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return update, errmsgs.WrapError(err)
}
if instance.InstanceType == d.Get("instance_type").(string) {
break
}
timeout = timeout - DefaultIntervalShort
if timeout <= 0 {
return update, errmsgs.WrapErrorf(err, errmsgs.WaitTimeoutMsg, d.Id(), GetFunc(1), timeout, instance.InstanceType, d.Get("instance_type"), errmsgs.ProviderERROR)
}
time.Sleep(DefaultIntervalShort * time.Second)
}
//d.SetPartial("instance_type")
}
return update, nil
}
func modifyInstanceNetworkSpec(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
if d.IsNewResource() {
d.Partial(false)
return nil
}
allocate := false
update := false
request := ecs.CreateModifyInstanceNetworkSpecRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
request.ClientToken = buildClientToken(request.GetActionName())
if d.HasChange("internet_max_bandwidth_out") {
o, n := d.GetChange("internet_max_bandwidth_out")
if o.(int) <= 0 && n.(int) > 0 {
allocate = true
}
request.InternetMaxBandwidthOut = requests.NewInteger(n.(int))
update = true
//d.SetPartial("internet_max_bandwidth_out")
}
if d.HasChange("internet_max_bandwidth_in") {
request.InternetMaxBandwidthIn = requests.NewInteger(d.Get("internet_max_bandwidth_in").(int))
update = true
//d.SetPartial("internet_max_bandwidth_in")
}
//An instance that was successfully modified once cannot be modified again within 5 minutes.
wait := incrementalWait(2*time.Second, 2*time.Second)
if update {
if err := resource.Retry(d.Timeout(schema.TimeoutUpdate), func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ModifyInstanceNetworkSpec(request)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{errmsgs.Throttling, "LastOrderProcessing", "LastRequestProcessing", "LastTokenProcessing"}) {
wait()
return resource.RetryableError(err)
}
if errmsgs.IsExpectedErrors(err, []string{"InternalError"}) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
}); err != nil {
return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
ecsService := EcsService{client: client}
deadline := time.Now().Add(DefaultTimeout * time.Second)
for {
instance, err := ecsService.DescribeInstance(d.Id())
if err != nil {
return errmsgs.WrapError(err)
}
if instance.InternetMaxBandwidthOut == d.Get("internet_max_bandwidth_out").(int) &&
instance.InternetMaxBandwidthIn == d.Get("internet_max_bandwidth_in").(int) {
break
}
if time.Now().After(deadline) {
return errmsgs.WrapError(errmsgs.Error(`wait for internet update timeout! expect internet_charge_type value %s, get %s
expect internet_max_bandwidth_out value %d, get %d, expect internet_max_bandwidth_out value %d, get %d,`,
//d.Get("internet_charge_type").(string),
"default",
instance.InternetChargeType, d.Get("internet_max_bandwidth_out").(int),
instance.InternetMaxBandwidthOut, d.Get("internet_max_bandwidth_in").(int), instance.InternetMaxBandwidthIn))
}
time.Sleep(1 * time.Second)
}
if allocate {
request := ecs.CreateAllocatePublicIpAddressRequest()
client.InitRpcRequest(*request.RpcRequest)
request.InstanceId = d.Id()
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.AllocatePublicIpAddress(request)
})
if err != nil {
return errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
}
return nil
}
func AssignIpv6AddressesFunc(id string, ipv6_addresses_count int, ipv6_addresses []string, meta interface{}) ([]string, error) {
client := meta.(*connectivity.AlibabacloudStackClient)
ecsService := EcsService{client: client}
var ipv6s []string
request := ecs.CreateAssignIpv6AddressesRequest()
client.InitRpcRequest(*request.RpcRequest)
instance, err := ecsService.DescribeInstance(id)
if err != nil {
return ipv6s, errmsgs.WrapErrorf(err, errmsgs.DefaultErrorMsg, id, "DescribeInstance", errmsgs.AlibabacloudStackSdkGoERROR)
}
request.NetworkInterfaceId = instance.NetworkInterfaces.NetworkInterface[0].NetworkInterfaceId
request.Ipv6AddressCount = requests.NewInteger(ipv6_addresses_count)
request.Ipv6Address = &ipv6_addresses
raw, ipv6_err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.AssignIpv6Addresses(request)
})
if ipv6_err != nil {
return ipv6s, errmsgs.WrapErrorf(ipv6_err, errmsgs.DefaultErrorMsg, "AssignIpv6Addresses", errmsgs.AlibabacloudStackSdkGoERROR)
}
addDebug("AssignIpv6Addresses", raw, request.RpcRequest, request)
response, _ := raw.(*ecs.AssignIpv6AddressesResponse)
ipv6s = response.Ipv6Sets.Ipv6Address
return ipv6s, nil
}
func ecsMergeTags(d *schema.ResourceData, disktags map[string]interface{}) map[string]interface{} {
if intance_tags, ok := d.GetOk("tags"); ok && len(intance_tags.(map[string]interface{})) > 0 {
mergedMap := make(map[string]interface{})
for k, v := range intance_tags.(map[string]interface{}) {
mergedMap[k] = v
}
for k, v := range disktags {
mergedMap[k] = v
}
return mergedMap
}
return disktags
}
func getOnlySystemTags(d *schema.ResourceData, tags []ecs.Tag) []ecs.Tag {
var only_system_tags []ecs.Tag
old_s_tags := d.Get("system_disk_tags").(map[string]interface{})
ecs_tags := d.Get("tags").(map[string]interface{})
only_ecs_tags := make([]string, 0)
// 获取只属于ecs的tags 的key列表
for k, _ := range ecs_tags {
if _, ok := old_s_tags[k]; !ok {
only_ecs_tags = append(only_ecs_tags, k)
}
}
// 剔除只属于ecs的tags
for _, tag := range tags {
in_only_ecs_tags := false
for _, only_ecs_tag := range only_ecs_tags {
if tag.TagKey == only_ecs_tag {
in_only_ecs_tags = true
break
}
}
if !in_only_ecs_tags {
only_system_tags = append(only_system_tags, tag)
}
}
return only_system_tags
}