github.com/rmenn/terraform@v0.3.8-0.20150225065417-fc84b3a78802/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/gen/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  	}
   123  }
   124  
   125  func resourceAwsAutoscalingGroupCreate(d *schema.ResourceData, meta interface{}) error {
   126  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   127  
   128  	var autoScalingGroupOpts autoscaling.CreateAutoScalingGroupType
   129  	autoScalingGroupOpts.AutoScalingGroupName = aws.String(d.Get("name").(string))
   130  	autoScalingGroupOpts.HealthCheckType = aws.String(d.Get("health_check_type").(string))
   131  	autoScalingGroupOpts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   132  	autoScalingGroupOpts.MinSize = aws.Integer(d.Get("min_size").(int))
   133  	autoScalingGroupOpts.MaxSize = aws.Integer(d.Get("max_size").(int))
   134  	autoScalingGroupOpts.AvailabilityZones = expandStringList(
   135  		d.Get("availability_zones").(*schema.Set).List())
   136  
   137  	if v, ok := d.GetOk("default_cooldown"); ok {
   138  		autoScalingGroupOpts.DefaultCooldown = aws.Integer(v.(int))
   139  	}
   140  
   141  	if v, ok := d.GetOk("desired_capacity"); ok {
   142  		autoScalingGroupOpts.DesiredCapacity = aws.Integer(v.(int))
   143  	}
   144  
   145  	if v, ok := d.GetOk("health_check_grace_period"); ok {
   146  		autoScalingGroupOpts.HealthCheckGracePeriod = aws.Integer(v.(int))
   147  	}
   148  
   149  	if v, ok := d.GetOk("load_balancers"); ok && v.(*schema.Set).Len() > 0 {
   150  		autoScalingGroupOpts.LoadBalancerNames = expandStringList(
   151  			v.(*schema.Set).List())
   152  	}
   153  
   154  	if v, ok := d.GetOk("vpc_zone_identifier"); ok && v.(*schema.Set).Len() > 0 {
   155  		exp := expandStringList(v.(*schema.Set).List())
   156  		autoScalingGroupOpts.VPCZoneIdentifier = aws.String(strings.Join(exp, ","))
   157  	}
   158  
   159  	if v, ok := d.GetOk("termination_policies"); ok && v.(*schema.Set).Len() > 0 {
   160  		autoScalingGroupOpts.TerminationPolicies = expandStringList(
   161  			v.(*schema.Set).List())
   162  	}
   163  
   164  	log.Printf("[DEBUG] AutoScaling Group create configuration: %#v", autoScalingGroupOpts)
   165  	err := autoscalingconn.CreateAutoScalingGroup(&autoScalingGroupOpts)
   166  	if err != nil {
   167  		return fmt.Errorf("Error creating Autoscaling Group: %s", err)
   168  	}
   169  
   170  	d.SetId(d.Get("name").(string))
   171  	log.Printf("[INFO] AutoScaling Group ID: %s", d.Id())
   172  
   173  	return resourceAwsAutoscalingGroupRead(d, meta)
   174  }
   175  
   176  func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) error {
   177  	g, err := getAwsAutoscalingGroup(d, meta)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	if g == nil {
   182  		return nil
   183  	}
   184  
   185  	d.Set("availability_zones", g.AvailabilityZones)
   186  	d.Set("default_cooldown", *g.DefaultCooldown)
   187  	d.Set("desired_capacity", *g.DesiredCapacity)
   188  	d.Set("health_check_grace_period", *g.HealthCheckGracePeriod)
   189  	d.Set("health_check_type", *g.HealthCheckType)
   190  	d.Set("launch_configuration", *g.LaunchConfigurationName)
   191  	d.Set("load_balancers", g.LoadBalancerNames)
   192  	d.Set("min_size", *g.MinSize)
   193  	d.Set("max_size", *g.MaxSize)
   194  	d.Set("name", *g.AutoScalingGroupName)
   195  	d.Set("vpc_zone_identifier", strings.Split(*g.VPCZoneIdentifier, ","))
   196  	d.Set("termination_policies", g.TerminationPolicies)
   197  
   198  	return nil
   199  }
   200  
   201  func resourceAwsAutoscalingGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   202  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   203  
   204  	opts := autoscaling.UpdateAutoScalingGroupType{
   205  		AutoScalingGroupName: aws.String(d.Id()),
   206  	}
   207  
   208  	if d.HasChange("desired_capacity") {
   209  		opts.DesiredCapacity = aws.Integer(d.Get("desired_capacity").(int))
   210  	}
   211  
   212  	if d.HasChange("launch_configuration") {
   213  		opts.LaunchConfigurationName = aws.String(d.Get("launch_configuration").(string))
   214  	}
   215  
   216  	if d.HasChange("min_size") {
   217  		opts.MinSize = aws.Integer(d.Get("min_size").(int))
   218  	}
   219  
   220  	if d.HasChange("max_size") {
   221  		opts.MaxSize = aws.Integer(d.Get("max_size").(int))
   222  	}
   223  
   224  	log.Printf("[DEBUG] AutoScaling Group update configuration: %#v", opts)
   225  	err := autoscalingconn.UpdateAutoScalingGroup(&opts)
   226  	if err != nil {
   227  		d.Partial(true)
   228  		return fmt.Errorf("Error updating Autoscaling group: %s", err)
   229  	}
   230  
   231  	return resourceAwsAutoscalingGroupRead(d, meta)
   232  }
   233  
   234  func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) error {
   235  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   236  
   237  	// Read the autoscaling group first. If it doesn't exist, we're done.
   238  	// We need the group in order to check if there are instances attached.
   239  	// If so, we need to remove those first.
   240  	g, err := getAwsAutoscalingGroup(d, meta)
   241  	if err != nil {
   242  		return err
   243  	}
   244  	if g == nil {
   245  		return nil
   246  	}
   247  	if len(g.Instances) > 0 || *g.DesiredCapacity > 0 {
   248  		if err := resourceAwsAutoscalingGroupDrain(d, meta); err != nil {
   249  			return err
   250  		}
   251  	}
   252  
   253  	log.Printf("[DEBUG] AutoScaling Group destroy: %v", d.Id())
   254  	deleteopts := autoscaling.DeleteAutoScalingGroupType{AutoScalingGroupName: aws.String(d.Id())}
   255  
   256  	// You can force an autoscaling group to delete
   257  	// even if it's in the process of scaling a resource.
   258  	// Normally, you would set the min-size and max-size to 0,0
   259  	// and then delete the group. This bypasses that and leaves
   260  	// resources potentially dangling.
   261  	if d.Get("force_delete").(bool) {
   262  		deleteopts.ForceDelete = aws.Boolean(true)
   263  	}
   264  
   265  	if err := autoscalingconn.DeleteAutoScalingGroup(&deleteopts); err != nil {
   266  		autoscalingerr, ok := err.(aws.APIError)
   267  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   268  			return nil
   269  		}
   270  		return err
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  func getAwsAutoscalingGroup(
   277  	d *schema.ResourceData,
   278  	meta interface{}) (*autoscaling.AutoScalingGroup, error) {
   279  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   280  
   281  	describeOpts := autoscaling.AutoScalingGroupNamesType{
   282  		AutoScalingGroupNames: []string{d.Id()},
   283  	}
   284  
   285  	log.Printf("[DEBUG] AutoScaling Group describe configuration: %#v", describeOpts)
   286  	describeGroups, err := autoscalingconn.DescribeAutoScalingGroups(&describeOpts)
   287  	if err != nil {
   288  		autoscalingerr, ok := err.(aws.APIError)
   289  		if ok && autoscalingerr.Code == "InvalidGroup.NotFound" {
   290  			d.SetId("")
   291  			return nil, nil
   292  		}
   293  
   294  		return nil, fmt.Errorf("Error retrieving AutoScaling groups: %s", err)
   295  	}
   296  
   297  	// Search for the autoscaling group
   298  	for idx, asc := range describeGroups.AutoScalingGroups {
   299  		if *asc.AutoScalingGroupName == d.Id() {
   300  			return &describeGroups.AutoScalingGroups[idx], nil
   301  		}
   302  	}
   303  
   304  	// ASG not found
   305  	d.SetId("")
   306  	return nil, nil
   307  }
   308  
   309  func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{}) error {
   310  	autoscalingconn := meta.(*AWSClient).autoscalingconn
   311  
   312  	// First, set the capacity to zero so the group will drain
   313  	log.Printf("[DEBUG] Reducing autoscaling group capacity to zero")
   314  	opts := autoscaling.UpdateAutoScalingGroupType{
   315  		AutoScalingGroupName: aws.String(d.Id()),
   316  		DesiredCapacity:      aws.Integer(0),
   317  		MinSize:              aws.Integer(0),
   318  		MaxSize:              aws.Integer(0),
   319  	}
   320  	if err := autoscalingconn.UpdateAutoScalingGroup(&opts); err != nil {
   321  		return fmt.Errorf("Error setting capacity to zero to drain: %s", err)
   322  	}
   323  
   324  	// Next, wait for the autoscale group to drain
   325  	log.Printf("[DEBUG] Waiting for group to have zero instances")
   326  	return resource.Retry(10*time.Minute, func() error {
   327  		g, err := getAwsAutoscalingGroup(d, meta)
   328  		if err != nil {
   329  			return resource.RetryError{Err: err}
   330  		}
   331  		if g == nil {
   332  			return nil
   333  		}
   334  
   335  		if len(g.Instances) == 0 {
   336  			return nil
   337  		}
   338  
   339  		return fmt.Errorf("group still has %d instances", len(g.Instances))
   340  	})
   341  }