alibabacloudstack/resource_apsarastack_ess_attachment.go (267 lines of code) (raw):
package alibabacloudstack
import (
"encoding/json"
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ess"
"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 resourceAlibabacloudstackEssAttachment() *schema.Resource {
resource := &schema.Resource{
Schema: map[string]*schema.Schema{
"scaling_group_id": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"instance_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
MaxItems: 20,
MinItems: 1,
},
"force": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
setResourceFunc(resource,
resourceAlibabacloudstackEssAttachmentCreate,
resourceAlibabacloudstackEssAttachmentRead,
resourceAlibabacloudstackEssAttachmentUpdate,
resourceAlibabacloudstackEssAttachmentDelete)
return resource
}
func resourceAlibabacloudstackEssAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
d.SetId(d.Get("scaling_group_id").(string))
return nil
}
func resourceAlibabacloudstackEssAttachmentUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
essService := EssService{client}
d.Partial(true)
if d.HasChange("instance_ids") {
object, err := essService.DescribeEssScalingGroup(d.Id())
if err != nil {
return errmsgs.WrapError(err)
}
if object.LifecycleState == string(Inactive) {
return errmsgs.WrapError(errmsgs.Error("Scaling group current status is %s, please active it before attaching or removing ECS instances.", object.LifecycleState))
} else {
if err := essService.WaitForEssScalingGroup(object.ScalingGroupId, Active, DefaultTimeout); err != nil {
return errmsgs.WrapError(err)
}
}
o, n := d.GetChange("instance_ids")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := os.Difference(ns).List()
add := convertArrayInterfaceToArrayString(ns.Difference(os).List())
if len(add) > 0 {
request := ess.CreateAttachInstancesRequest()
client.InitRpcRequest(*request.RpcRequest)
request.ScalingGroupId = d.Id()
request.InstanceId = &add
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEssClient(func(essClient *ess.Client) (interface{}, error) {
return essClient.AttachInstances(request)
})
response, ok := raw.(*ess.AttachInstancesResponse)
if err != nil {
errmsg := ""
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(response.BaseResponse)
}
raw_data := make(map[string]interface{})
err := json.Unmarshal(response.BaseResponse.GetHttpContentBytes(), &raw_data)
errCode := raw_data["errorCode"].(string)
if errCode == "IncorrectCapacity.MaxSize" {
instances, err := essService.DescribeEssAttachment(d.Id(), make([]string, 0))
if !errmsgs.NotFoundError(err) {
return resource.NonRetryableError(err)
}
var autoAdded, attached []string
if len(instances) > 0 {
for _, inst := range instances {
if inst.CreationType == "Attached" {
attached = append(attached, inst.InstanceId)
} else {
autoAdded = append(autoAdded, inst.InstanceId)
}
}
}
if len(add) > object.MaxSize {
return resource.NonRetryableError(errmsgs.WrapError(errmsgs.Error("To attach %d instances, the total capacity will be greater than the scaling group max size %d. "+
"Please enlarge scaling group max size.", len(add), object.MaxSize)))
}
if len(autoAdded) > 0 {
if d.Get("force").(bool) {
if err := essService.EssRemoveInstances(d.Id(), autoAdded); err != nil {
return resource.NonRetryableError(errmsgs.WrapError(err))
}
time.Sleep(5)
return resource.RetryableError(errmsgs.WrapError(err))
} else {
return resource.NonRetryableError(errmsgs.WrapError(errmsgs.Error("To attach the instances, the total capacity will be greater than the scaling group max size %d."+
"Please enlarge scaling group max size or set 'force' to true to remove autocreated instances: %#v.", object.MaxSize, autoAdded)))
}
}
if len(attached) > 0 {
return resource.NonRetryableError(errmsgs.WrapError(errmsgs.Error("To attach the instances, the total capacity will be greater than the scaling group max size %d. "+
"Please enlarge scaling group max size or remove already attached instances: %#v.", object.MaxSize, attached)))
}
}
if errCode == "ScalingActivityInProgress" {
time.Sleep(5)
return resource.RetryableError(errmsgs.WrapError(err))
}
return resource.NonRetryableError(errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_ess_attachment", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg))
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
return errmsgs.WrapError(err)
}
err = resource.Retry(3*time.Minute, func() *resource.RetryError {
instances, err := essService.DescribeEssAttachment(d.Id(), add)
if err != nil {
return resource.NonRetryableError(errmsgs.WrapError(err))
}
if len(instances) < 0 {
return resource.RetryableError(errmsgs.WrapError(errmsgs.Error("There are no ECS instances have been attached.")))
}
for _, inst := range instances {
if inst.LifecycleState != string(InService) {
return resource.RetryableError(errmsgs.WrapError(errmsgs.Error("There are still ECS instances are not %s.", string(InService))))
}
}
return nil
})
if err != nil {
return errmsgs.WrapError(err)
}
}
if len(remove) > 0 {
if err := essService.EssRemoveInstances(d.Id(), convertArrayInterfaceToArrayString(remove)); err != nil {
return errmsgs.WrapError(err)
}
}
//d.SetPartial("instance_ids")
}
d.Partial(false)
return nil
}
func resourceAlibabacloudstackEssAttachmentRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
essService := EssService{client}
object, err := essService.DescribeEssAttachment(d.Id(), make([]string, 0))
if err != nil {
if errmsgs.NotFoundError(err) {
d.SetId("")
return nil
}
return errmsgs.WrapError(err)
}
var instanceIds []string
for _, inst := range object {
instanceIds = append(instanceIds, inst.InstanceId)
}
d.Set("scaling_group_id", object[0].ScalingGroupId)
d.Set("instance_ids", instanceIds)
return nil
}
func resourceAlibabacloudstackEssAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
essService := EssService{client}
removed := convertArrayInterfaceToArrayString(d.Get("instance_ids").(*schema.Set).List())
if len(removed) < 1 {
return nil
}
object, err := essService.DescribeEssScalingGroup(d.Id())
if err != nil {
return errmsgs.WrapError(err)
}
if err := essService.WaitForEssScalingGroup(object.ScalingGroupId, Active, DefaultTimeout); err != nil {
if errmsgs.NotFoundError(err) {
return nil
}
return errmsgs.WrapError(err)
}
if err := resource.Retry(5*time.Minute, func() *resource.RetryError {
request := ess.CreateRemoveInstancesRequest()
client.InitRpcRequest(*request.RpcRequest)
request.ScalingGroupId = d.Id()
if len(removed) > 0 {
request.InstanceId = &removed
} else {
return nil
}
raw, err := essService.client.WithEssClient(func(essClient *ess.Client) (interface{}, error) {
return essClient.RemoveInstances(request)
})
response, ok := raw.(*ess.RemoveInstancesResponse)
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
if err != nil {
raw_data := make(map[string]interface{})
err := json.Unmarshal(response.BaseResponse.GetHttpContentBytes(), &raw_data)
errmsg := ""
if err != nil && ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(response.BaseResponse)
}
errCode := raw_data["errorCode"].(string)
if errCode == "IncorrectCapacity.MinSize" {
instances, err := essService.DescribeEssAttachment(d.Id(), removed)
if len(instances) > 0 {
if object.MinSize == 0 {
return resource.RetryableError(errmsgs.WrapError(err))
}
return resource.NonRetryableError(errmsgs.WrapError(errmsgs.Error("To remove %d instances, the total capacity will be lesser than the scaling group min size %d. "+
"Please shorten scaling group min size and try again.", len(removed), object.MinSize)))
}
}
if errCode == "ScalingActivityInProgress" || errCode == "IncorrectScalingGroupStatus" {
time.Sleep(5)
return resource.RetryableError(errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_ess_attachment", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg))
}
if errCode == "InvalidScalingGroupId.NotFound" {
return nil
}
if err != nil {
return resource.NonRetryableError(errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_ess_attachment", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg))
}
}
time.Sleep(3 * time.Second)
instances, err := essService.DescribeEssAttachment(d.Id(), removed)
if err != nil {
if errmsgs.NotFoundError(err) {
return nil
}
return resource.NonRetryableError(errmsgs.WrapError(err))
}
if len(instances) > 0 {
removed = make([]string, 0)
for _, inst := range instances {
removed = append(removed, inst.InstanceId)
}
return resource.RetryableError(errmsgs.WrapError(errmsgs.Error("There are still ECS instances in the scaling group.")))
}
return nil
}); err != nil {
return errmsgs.WrapError(err)
}
return errmsgs.WrapError(essService.WaitForEssAttachment(d.Id(), Deleted, DefaultTimeout))
}
func convertArrayInterfaceToArrayString(elm []interface{}) (arr []string) {
if len(elm) < 1 {
return
}
for _, e := range elm {
arr = append(arr, e.(string))
}
return
}