Skip to content

Commit

Permalink
Merge pull request #266 from alibaba/dev
Browse files Browse the repository at this point in the history
 add ram, keypair and userdata for ess
  • Loading branch information
demonwy authored Nov 10, 2017
2 parents 4458bdd + bac2c59 commit 92413fe
Show file tree
Hide file tree
Showing 11 changed files with 535 additions and 158 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## 1.3.0 (unreleased)

## 1.2.7 (November 10, 2017)
IMPROVMENTS:
* add keypair, ram and userdata for ess scaling configuration ([#265](https://github.com/alibaba/terraform-provider/pull/265))
* add force_delete to delete scaling configuration when only one configuration is existing ([#265](https://github.com/alibaba/terraform-provider/pull/265))
* add substitute to active another scaling configuration ([#265](https://github.com/alibaba/terraform-provider/pull/265))

## 1.2.6 (October 17, 2017)
IMPROVMENTS:
* modify CPU to Core(s) and improve some test case ([#254](https://github.com/alibaba/terraform-provider/pull/254))
Expand Down
12 changes: 12 additions & 0 deletions alicloud/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"encoding/base64"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/terraform/helper/schema"
Expand Down Expand Up @@ -144,3 +145,14 @@ func (client *AliyunClient) JudgeRegionValidation(key string, region common.Regi
}
return fmt.Errorf("'%s' is invalid. Expected on %v.", key, strings.Join(rs, ", "))
}

func userDataHashSum(user_data string) string {
// Check whether the user_data is not Base64 encoded.
// Always calculate hash of base64 decoded value since we
// check against double-encoding when setting it
v, base64DecodeError := base64.StdEncoding.DecodeString(user_data)
if base64DecodeError != nil {
v = []byte(user_data)
}
return string(v)
}
1 change: 1 addition & 0 deletions alicloud/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
// ess
InvalidScalingGroupIdNotFound = "InvalidScalingGroupId.NotFound"
IncorrectScalingConfigurationLifecycleState = "IncorrectScalingConfigurationLifecycleState"
IncorrectScalingGroupStatus = "IncorrectScalingGroupStatus"

// oss
OssBucketNotFound = "NoSuchBucket"
Expand Down
256 changes: 222 additions & 34 deletions alicloud/resource_alicloud_ess_scalingconfiguration.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,36 @@ func resourceAlicloudEssScalingConfiguration() *schema.Resource {
Optional: true,
MaxItems: 20,
},

"substitute": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"user_data": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"role_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"key_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"force_delete": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}
Expand All @@ -145,72 +175,142 @@ func resourceAliyunEssScalingConfigurationCreate(d *schema.ResourceData, meta in
essconn := meta.(*AliyunClient).essconn

scaling, err := essconn.CreateScalingConfiguration(args)
if err != nil {
return err
if err != nil && !IsExceptedError(err, IncorrectScalingGroupStatus) {
return fmt.Errorf("Error Create Scaling Configuration: %#v", err)
}

d.SetId(d.Get("scaling_group_id").(string) + COLON_SEPARATED + scaling.ScalingConfigurationId)
d.SetId(scaling.ScalingConfigurationId)

return resourceAliyunEssScalingConfigurationUpdate(d, meta)
}

func resourceAliyunEssScalingConfigurationUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AliyunClient)
d.Partial(true)
if strings.Contains(d.Id(), COLON_SEPARATED) {
d.SetId(strings.Split(d.Id(), COLON_SEPARATED)[1])
}

if d.HasChange("active") {
active := d.Get("active").(bool)
if !active {
return fmt.Errorf("Please active the scaling configuration directly.")
c, err := client.DescribeScalingConfigurationById(d.Id())
if err != nil {
if NotFoundError(err) {
d.SetId("")
return nil
}
return fmt.Errorf("Error Describe ESS scaling configuration Attribute: %#v", err)
}
ids := strings.Split(d.Id(), COLON_SEPARATED)
err := client.ActiveScalingConfigurationById(ids[0], ids[1])

if err != nil {
return fmt.Errorf("Active scaling configuration %s err: %#v", ids[1], err)
active := d.Get("active").(bool)

if active {
if c.LifecycleState == ess.Inacitve {

err := client.ActiveScalingConfigurationById(c.ScalingGroupId, d.Id())
if err != nil {
return fmt.Errorf("Active scaling configuration %s err: %#v", d.Id(), err)
}
}
} else {
if c.LifecycleState == ess.Active {
_, err := activeSubstituteScalingConfiguration(d, meta)
if err != nil {
return err
}
}
}
d.SetPartial("active")
}

if err := enableEssScalingConfiguration(d, meta); err != nil {
return err
}

if d.HasChange("instance_ids") {
sgId := d.Get("scaling_group_id").(string)
if _, err := client.essconn.EnableScalingGroup(&ess.EnableScalingGroupArgs{
ScalingGroupId: sgId,
InstanceId: expandStringList(d.Get("instance_ids").([]interface{})),
}); err != nil {
return fmt.Errorf("EnableScalingGroup %s got an error: %#v", sgId, err)
}

d.SetPartial("instance_ids")
}

d.Partial(false)

return resourceAliyunEssScalingConfigurationRead(d, meta)
}

func enableEssScalingConfiguration(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AliyunClient)
ids := strings.Split(d.Id(), COLON_SEPARATED)

if d.HasChange("enable") {
d.SetPartial("enable")
sgId := d.Get("scaling_group_id").(string)
group, err := client.DescribeScalingGroupById(sgId)
if err != nil {
return fmt.Errorf("DescribeScalingGroupById %s error: %#v", sgId, err)
}
enable := d.Get("enable").(bool)
if !enable {
err := client.DisableScalingConfigurationById(ids[0])

if err != nil {
return fmt.Errorf("Disable scaling group %s err: %#v", ids[0], err)
if enable {
if group.LifecycleState == ess.Inacitve {

cs, _, err := client.essconn.DescribeScalingConfigurations(&ess.DescribeScalingConfigurationsArgs{
RegionId: getRegion(d, meta),
ScalingGroupId: sgId,
Pagination: getPagination(1, 50),
})

if err != nil {
fmt.Errorf("Describe ScalingConfigurations by scaling group %s got an error: %#v", sgId, err)
}
activeConfig := ""
var csIds []string
for _, c := range cs {
csIds = append(csIds, c.ScalingConfigurationId)
if c.LifecycleState == ess.Active {
activeConfig = c.ScalingConfigurationId
}
}

if activeConfig == "" {
return fmt.Errorf("Please active a scaling configuration before enabling scaling group %s. "+
"Its all scaling configuration are %s.", sgId, strings.Join(csIds, ","))
}

if _, err := client.essconn.EnableScalingGroup(&ess.EnableScalingGroupArgs{
ScalingGroupId: sgId,
ActiveScalingConfigurationId: activeConfig,
}); err != nil {
return fmt.Errorf("EnableScalingGroup %s got an error: %#v", sgId, err)
}

d.SetPartial("scaling_configuration_id")
}
} else {
if group.LifecycleState == ess.Active {
if _, err := client.essconn.DisableScalingGroup(&ess.DisableScalingGroupArgs{
ScalingGroupId: sgId,
}); err != nil {
return fmt.Errorf("DisableScalingGroup %s got an error: %#v", sgId, err)
}
}
}

instance_ids := []string{}
if d.HasChange("instance_ids") {
d.SetPartial("instance_ids")
instances := d.Get("instance_ids").([]interface{})
instance_ids = expandStringList(instances)
}
err := client.EnableScalingConfigurationById(ids[0], ids[1], instance_ids)

if err != nil {
return fmt.Errorf("Enable scaling configuration %s err: %#v", ids[1], err)
}
d.SetPartial("enable")
}

return nil
}

func resourceAliyunEssScalingConfigurationRead(d *schema.ResourceData, meta interface{}) error {

client := meta.(*AliyunClient)
ids := strings.Split(d.Id(), COLON_SEPARATED)
c, err := client.DescribeScalingConfigurationById(ids[0], ids[1])
if strings.Contains(d.Id(), COLON_SEPARATED) {
d.SetId(strings.Split(d.Id(), COLON_SEPARATED)[1])
}
c, err := client.DescribeScalingConfigurationById(d.Id())
if err != nil {
if NotFoundError(err) {
d.SetId("")
Expand All @@ -230,37 +330,71 @@ func resourceAliyunEssScalingConfigurationRead(d *schema.ResourceData, meta inte
d.Set("internet_max_bandwidth_out", c.InternetMaxBandwidthOut)
d.Set("system_disk_category", c.SystemDiskCategory)
d.Set("data_disk", flattenDataDiskMappings(c.DataDisks.DataDisk))
d.Set("role_name", c.RamRoleName)
d.Set("key_name", c.KeyPairName)
d.Set("user_data", userDataHashSum(c.UserData))
d.Set("force_delete", d.Get("force_delete").(bool))

return nil
}

func resourceAliyunEssScalingConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*AliyunClient)

if strings.Contains(d.Id(), COLON_SEPARATED) {
d.SetId(strings.Split(d.Id(), COLON_SEPARATED)[1])
}

configs, _ := activeSubstituteScalingConfiguration(d, meta)
if len(configs) <= 1 {
if len(configs) == 0 {
return nil
}
if d.Get("force_delete").(bool) {
return client.DeleteScalingGroupById(configs[0].ScalingGroupId)
}
return fmt.Errorf("Current scaling configuration %s is the last configuration for the scaling group %s. Please launch a new "+
"active scaling configuration or set 'force_delete' to 'true' to delete it with deleting its scaling group.", d.Id(), configs[0].ScalingGroupId)
}

return resource.Retry(5*time.Minute, func() *resource.RetryError {
ids := strings.Split(d.Id(), COLON_SEPARATED)
err := client.DeleteScalingConfigurationById(ids[0], ids[1])

_, err := client.essconn.DeleteScalingConfiguration(&ess.DeleteScalingConfigurationArgs{
ScalingConfigurationId: d.Id(),
})

if err != nil {
e, _ := err.(*common.Error)
if e.ErrorResponse.Code == IncorrectScalingConfigurationLifecycleState {
return resource.NonRetryableError(
fmt.Errorf("Scaling configuration is active - please active another one and trying again."))
fmt.Errorf("Scaling configuration is active. Please active another one before deleting it and trying again."))
}
if e.ErrorResponse.Code != InvalidScalingGroupIdNotFound {
return resource.RetryableError(
fmt.Errorf("Scaling configuration in use - trying again while it is deleted."))
}
}

_, err = client.DescribeScalingConfigurationById(ids[0], ids[1])
c, err := client.DescribeScalingConfigurationById(d.Id())
if err != nil {
if NotFoundError(err) {
return nil
}
return resource.NonRetryableError(err)
}

instances, _, err := client.essconn.DescribeScalingInstances(&ess.DescribeScalingInstancesArgs{
RegionId: getRegion(d, meta),
ScalingGroupId: c.ScalingGroupId,
ScalingConfigurationId: d.Id(),
})
if err != nil {
return resource.NonRetryableError(err)
}
if len(instances) > 0 {
return resource.NonRetryableError(fmt.Errorf("There are still ECS instances in the scaling configuration - please remove them and try again."))
}

return resource.RetryableError(
fmt.Errorf("Scaling configuration in use - trying again while it is deleted."))
})
Expand Down Expand Up @@ -320,5 +454,59 @@ func buildAlicloudEssScalingConfigurationArgs(d *schema.ResourceData, meta inter
args.DataDisk = diskTypes
}

if v, ok := d.GetOk("role_name"); ok && v.(string) != "" {
args.RamRoleName = v.(string)
}

if v, ok := d.GetOk("key_name"); ok && v.(string) != "" {
args.KeyPairName = v.(string)
}

if v, ok := d.GetOk("user_data"); ok && v.(string) != "" {
args.UserData = v.(string)
}

return args, nil
}

func activeSubstituteScalingConfiguration(d *schema.ResourceData, meta interface{}) ([]ess.ScalingConfigurationItemType, error) {
client := meta.(*AliyunClient)
substitute_id, ok := d.GetOk("substitute")

c, err := client.DescribeScalingConfigurationById(d.Id())
if err != nil {
return nil, fmt.Errorf("DescribeScalingConfigurationById error: %#v", err)
}

cs, _, err := client.essconn.DescribeScalingConfigurations(&ess.DescribeScalingConfigurationsArgs{
RegionId: getRegion(d, meta),
ScalingGroupId: c.ScalingGroupId,
})
if err != nil {
return nil, fmt.Errorf("DescribeScalingConfigurations error: %#v", err)
}

if !ok || substitute_id.(string) == "" {

if len(cs) <= 1 {
return cs, fmt.Errorf("Current scaling configuration %s is the last configuration for the scaling group %s, and it can't be inactive.", d.Id(), c.ScalingGroupId)
}

var configs []string
for _, cc := range cs {
if cc.ScalingConfigurationId != d.Id() {
configs = append(configs, cc.ScalingConfigurationId)
}
}

return cs, fmt.Errorf("Before inactivating current scaling configuration, you must select a substitute for scaling group from: %s.", strings.Join(configs, ","))
}

err = client.ActiveScalingConfigurationById(c.ScalingGroupId, substitute_id.(string))
if err != nil {
return cs, fmt.Errorf("Inactive scaling configuration %s err: %#v. Substitute scaling configuration ID: %s",
d.Id(), err, substitute_id.(string))
}

return cs, nil
}
Loading

0 comments on commit 92413fe

Please sign in to comment.