github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_autoscaling_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/awslabs/aws-sdk-go/aws"
    14  	"github.com/awslabs/aws-sdk-go/service/autoscaling"
    15  )
    16  
    17  func resourceAwsAutoscalingGroup() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAwsAutoscalingGroupCreate,
    20  		Read:   resourceAwsAutoscalingGroupRead,
    21  		Update: resourceAwsAutoscalingGroupUpdate,
    22  		Delete: resourceAwsAutoscalingGroupDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": &schema.Schema{
    26  				Type:     schema.TypeString,
    27  				Required: true,
    28  				ForceNew: true,
    29  			},
    30  
    31  			"launch_configuration": &schema.Schema{
    32  				Type:     schema.TypeString,
    33  				Required: true,
    34  			},
    35  
    36  			"desired_capacity": &schema.Schema{
    37  				Type:     schema.TypeInt,
    38  				Optional: true,
    39  				Computed: true,
    40  			},
    41  
    42  			"min_size": &schema.Schema{
    43  				Type:     schema.TypeInt,
    44  				Required: true,
    45  			},
    46  
    47  			"max_size": &schema.Schema{
    48  				Type:     schema.TypeInt,
    49  				Required: true,
    50  			},
    51  
    52  			"default_cooldown": &schema.Schema{
    53  				Type:     schema.TypeInt,
    54  				Optional: true,
    55  				Computed: true,
    56  				ForceNew: true,
    57  			},
    58  
    59  			"force_delete": &schema.Schema{
    60  				Type:     schema.TypeBool,
    61  				Optional: true,
    62  				Computed: true,
    63  				ForceNew: true,
    64  			},
    65  
    66  			"health_check_grace_period": &schema.Schema{
    67  				Type:     schema.TypeInt,
    68  				Optional: true,
    69  				Computed: true,
    70  				ForceNew: true,
    71  			},
    72  
    73  			"health_check_type": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Optional: true,
    76  				Computed: true,
    77  				ForceNew: true,
    78  			},
    79  
    80  			"availability_zones": &schema.Schema{
    81  				Type:     schema.TypeSet,
    82  				Required: true,
    83  				ForceNew: true,
    84  				Elem:     &schema.Schema{Type: schema.TypeString},
    85  				Set: func(v interface{}) int {
    86  					return hashcode.String(v.(string))
    87  				},
    88  			},
    89  
    90  			"load_balancers": &schema.Schema{
    91  				Type:     schema.TypeSet,
    92  				Optional: true,
    93  				ForceNew: true,
    94  				Elem:     &schema.Schema{Type: schema.TypeString},
    95  				Set: func(v interface{}) int {
    96  					return hashcode.String(v.(string))
    97  				},
    98  			},
    99  
   100  			"vpc_zone_identifier": &schema.Schema{
   101  				Type:     schema.TypeSet,
   102  				Optional: true,
   103  				Computed: true,
   104  				ForceNew: true,
   105  				Elem:     &schema.Schema{Type: schema.TypeString},
   106  				Set: func(v interface{}) int {
   107  					return hashcode.String(v.(string))
   108  				},
   109  			},
   110  
   111  			"termination_policies": &schema.Schema{
   112  				Type:     schema.TypeSet,
   113  				Optional: true,
   114  				Computed: true,
   115  				ForceNew: true,
   116  				Elem:     &schema.Schema{Type: schema.TypeString},
   117  				Set: func(v interface{}) int {
   118  					return hashcode.String(v.(string))
   119  				},
   120  			},
   121  
   122  			"tag": autoscalingTagsSchema(),
   123  		},
   124  	}
   125  }
   126  
   127  func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
   128  	autoscalingconn := meta.(*AWSClient).asgconn
   129  
   130  	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupInput
   131  	autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string))
   132  	autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   133  	autoScalingGroupOpts.MinSize = aws.Long(int64(d.Get("min_size").(int)))
   134  	autoScalingGroupOpts.MaxSize = aws.Long(int64(d.Get("max_size").(int)))
   135  	autoScalingGroupOpts.AvailabilityZones = expandStringListSDK(
   136  		d.Get("availability_zones").(*schema.Set).List())
   137  
   138  	if v, ok := d.GetOk("tag"); ok {
   139  		autoScalingGroupOpts.Tags = autoscalingTagsFromMap(
   140  			setToMapByKey(v.(*schema.Set), "key"), d.Get("name").(string))
   141  	}
   142  
   143  	if v, ok := d.GetOk("default_cooldown"); ok {
   144  		autoScalingGroupOpts.DefaultCooldown = aws.Long(int64(v.(int)))
   145  	}
   146  
   147  	if v, ok := d.GetOk("health_check_type"); ok && v.(string) != "" {
   148  		autoScalingGroupOpts.HealthCheckType = aws.String(v.(string))
   149  	}
   150  
   151  	if v, ok := d.GetOk("desired_capacity"); ok {
   152  		autoScalingGroupOpts.DesiredCapacity = aws.Long(int64(v.(int)))
   153  	}
   154  
   155  	if v, ok := d.GetOk("health_check_grace_period"); ok {
   156  		autoScalingGroupOpts.HealthCheckGracePeriod = aws.Long(int64(v.(int)))
   157  	}
   158  
   159  	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
   160  		autoScalingGroupOpts.LoadBalancerNames = expandStringListSDK(
   161  			v.(*schema.Set).List())
   162  	}
   163  
   164  	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
   165  		exp := expandStringListSDK(v.(*schema.Set).List())
   166  		strs := make([]string, len(exp))
   167  		for _, s := range exp {
   168  			strs = append(strs, *s)
   169  		}
   170  		autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(strs, ","))
   171  	}
   172  
   173  	if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 {
   174  		autoScalingGroupOpts.TerminationPolicies = expandStringListSDK(
   175  			v.(*schema.Set).List())
   176  	}
   177  
   178  	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
   179  	_, err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
   180  	if err != nil {
   181  		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
   182  	}
   183  
   184  	d.SetId(d.Get("name").(string))
   185  	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
   186  
   187  	return resourceAwsAutoscalingGroupRead(d, meta)
   188  }
   189  
   190  func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
   191  	g, err := getAwsAutoscalingGroup(d, meta)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	if g == nil {
   196  		return nil
   197  	}
   198  
   199  	d.Set("availability_zones", g.AvailabilityZones)
   200  	d.Set("default_cooldown", g.DefaultCooldown)
   201  	d.Set("desired_capacity", g.DesiredCapacity)
   202  	d.Set("health_check_grace_period", g.HealthCheckGracePeriod)
   203  	d.Set("health_check_type", g.HealthCheckType)
   204  	d.Set("launch_configuration", g.LaunchConfigurationName)
   205  	d.Set("load_balancers", g.LoadBalancerNames)
   206  	d.Set("min_size", g.MinSize)
   207  	d.Set("max_size", g.MaxSize)
   208  	d.Set("name", g.AutoScalingGroupName)
   209  	d.Set("tag", g.Tags)
   210  	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
   211  	d.Set("termination_policies", g.TerminationPolicies)
   212  
   213  	return nil
   214  }
   215  
   216  func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   217  	autoscalingconn := meta.(*AWSClient).asgconn
   218  
   219  	opts := autoscaling.UpdateAutoScalingGroupInput{
   220  		AutoScalingGroupName: aws.String(d.Id()),
   221  	}
   222  
   223  	if d.HasChange("desired_capacity") {
   224  		opts.DesiredCapacity = aws.Long(int64(d.Get("desired_capacity").(int)))
   225  	}
   226  
   227  	if d.HasChange("launch_configuration") {
   228  		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   229  	}
   230  
   231  	if d.HasChange("min_size") {
   232  		opts.MinSize = aws.Long(int64(d.Get("min_size").(int)))
   233  	}
   234  
   235  	if d.HasChange("max_size") {
   236  		opts.MaxSize = aws.Long(int64(d.Get("max_size").(int)))
   237  	}
   238  
   239  	if err := setAutoscalingTags(autoscalingconn, d); err != nil {
   240  		return err
   241  	} else {
   242  		d.SetPartial("tag")
   243  	}
   244  
   245  	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
   246  	_, err := autoscalingconn.UpdateAutoScalingGroup(&opts)
   247  	if err != nil {
   248  		d.Partial(true)
   249  		return fmt.Errorf("Error updating Autoscaling group: %s", err)
   250  	}
   251  
   252  	return resourceAwsAutoscalingGroupRead(d, meta)
   253  }
   254  
   255  func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
   256  	autoscalingconn := meta.(*AWSClient).asgconn
   257  
   258  	// Read the autoscaling group first. If it doesn't exist, we're done.
   259  	// We need the group in order to check if there are instances attached.
   260  	// If so, we need to remove those first.
   261  	g, err := getAwsAutoscalingGroup(d, meta)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	if g == nil {
   266  		return nil
   267  	}
   268  	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
   269  		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
   270  			return err
   271  		}
   272  	}
   273  
   274  	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
   275  	deleteopts := autoscaling.DeleteAutoScalingGroupInput{AutoScalingGroupName: aws.String(d.Id())}
   276  
   277  	// You can force an autoscaling group to delete
   278  	// even if it's in the process of scaling a resource.
   279  	// Normally, you would set the min-size and max-size to 0,0
   280  	// and then delete the group. This bypasses that and leaves
   281  	// resources potentially dangling.
   282  	if d.Get("force_delete").(bool) {
   283  		deleteopts.ForceDelete = aws.Boolean(true)
   284  	}
   285  
   286  	if _, err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
   287  		autoscalingerr, ok := err.(aws.APIError)
   288  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   289  			return nil
   290  		}
   291  		return err
   292  	}
   293  
   294  	return resource.Retry(5*time.Minute, func() error {
   295  		if g, _ = getAwsAutoscalingGroup(d, meta); g != nil {
   296  			return fmt.Errorf("Auto Scaling Group still exists")
   297  		}
   298  		return nil
   299  	})
   300  }
   301  
   302  func getAwsAutoscalingGroup(
   303  	d *schema.ResourceData,
   304  	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
   305  	autoscalingconn := meta.(*AWSClient).asgconn
   306  
   307  	describeOpts := autoscaling.DescribeAutoScalingGroupsInput{
   308  		AutoScalingGroupNames: []*string{aws.String(d.Id())},
   309  	}
   310  
   311  	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
   312  	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
   313  	if err != nil {
   314  		autoscalingerr, ok := err.(aws.APIError)
   315  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   316  			d.SetId("")
   317  			return nil, nil
   318  		}
   319  
   320  		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
   321  	}
   322  
   323  	// Search for the autoscaling group
   324  	for idx, asc := range describeGroups.AutoScalingGroups {
   325  		if *asc.AutoScalingGroupName == d.Id() {
   326  			return describeGroups.AutoScalingGroups[idx], nil
   327  		}
   328  	}
   329  
   330  	// ASG not found
   331  	d.SetId("")
   332  	return nil, nil
   333  }
   334  
   335  func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
   336  	autoscalingconn := meta.(*AWSClient).asgconn
   337  
   338  	// First, set the capacity to zero so the group will drain
   339  	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
   340  	opts := autoscaling.UpdateAutoScalingGroupInput{
   341  		AutoScalingGroupName: aws.String(d.Id()),
   342  		DesiredCapacity:      aws.Long(0),
   343  		MinSize:              aws.Long(0),
   344  		MaxSize:              aws.Long(0),
   345  	}
   346  	if _, err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
   347  		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
   348  	}
   349  
   350  	// Next, wait for the autoscale group to drain
   351  	log.Printf("[DEBUG] Waiting for group to have zero instances")
   352  	return resource.Retry(10*time.Minute, func() error {
   353  		g, err := getAwsAutoscalingGroup(d, meta)
   354  		if err != nil {
   355  			return resource.RetryError{Err: err}
   356  		}
   357  		if g == nil {
   358  			return nil
   359  		}
   360  
   361  		if len(g.Instances) == 0 {
   362  			return nil
   363  		}
   364  
   365  		return fmt.Errorf("group still has %d instances", len(g.Instances))
   366  	})
   367  }