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