github.com/rmenn/terraform@v0.3.8-0.20150225065417-fc84b3a78802/builtin/providers/aws/resource_aws_elb.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 8 "github.com/hashicorp/terraform/helper/hashcode" 9 "github.com/hashicorp/terraform/helper/schema" 10 "github.com/mitchellh/goamz/elb" 11 ) 12 13 func resourceAwsElb() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceAwsElbCreate, 16 Read: resourceAwsElbRead, 17 Update: resourceAwsElbUpdate, 18 Delete: resourceAwsElbDelete, 19 20 Schema: map[string]*schema.Schema{ 21 "name": &schema.Schema{ 22 Type: schema.TypeString, 23 Required: true, 24 ForceNew: true, 25 }, 26 27 "internal": &schema.Schema{ 28 Type: schema.TypeBool, 29 Optional: true, 30 ForceNew: true, 31 Computed: true, 32 }, 33 34 "cross_zone_load_balancing": &schema.Schema{ 35 Type: schema.TypeBool, 36 Optional: true, 37 }, 38 39 "availability_zones": &schema.Schema{ 40 Type: schema.TypeSet, 41 Elem: &schema.Schema{Type: schema.TypeString}, 42 Optional: true, 43 ForceNew: true, 44 Computed: true, 45 Set: func(v interface{}) int { 46 return hashcode.String(v.(string)) 47 }, 48 }, 49 50 "instances": &schema.Schema{ 51 Type: schema.TypeSet, 52 Elem: &schema.Schema{Type: schema.TypeString}, 53 Optional: true, 54 Computed: true, 55 Set: func(v interface{}) int { 56 return hashcode.String(v.(string)) 57 }, 58 }, 59 60 // TODO: could be not ForceNew 61 "security_groups": &schema.Schema{ 62 Type: schema.TypeSet, 63 Elem: &schema.Schema{Type: schema.TypeString}, 64 Optional: true, 65 ForceNew: true, 66 Computed: true, 67 Set: func(v interface{}) int { 68 return hashcode.String(v.(string)) 69 }, 70 }, 71 72 "subnets": &schema.Schema{ 73 Type: schema.TypeSet, 74 Elem: &schema.Schema{Type: schema.TypeString}, 75 Optional: true, 76 ForceNew: true, 77 Computed: true, 78 Set: func(v interface{}) int { 79 return hashcode.String(v.(string)) 80 }, 81 }, 82 83 "listener": &schema.Schema{ 84 Type: schema.TypeSet, 85 Required: true, 86 Elem: &schema.Resource{ 87 Schema: map[string]*schema.Schema{ 88 "instance_port": &schema.Schema{ 89 Type: schema.TypeInt, 90 Required: true, 91 }, 92 93 "instance_protocol": &schema.Schema{ 94 Type: schema.TypeString, 95 Required: true, 96 }, 97 98 "lb_port": &schema.Schema{ 99 Type: schema.TypeInt, 100 Required: true, 101 }, 102 103 "lb_protocol": &schema.Schema{ 104 Type: schema.TypeString, 105 Required: true, 106 }, 107 108 "ssl_certificate_id": &schema.Schema{ 109 Type: schema.TypeString, 110 Optional: true, 111 }, 112 }, 113 }, 114 Set: resourceAwsElbListenerHash, 115 }, 116 117 "health_check": &schema.Schema{ 118 Type: schema.TypeSet, 119 Optional: true, 120 Computed: true, 121 Elem: &schema.Resource{ 122 Schema: map[string]*schema.Schema{ 123 "healthy_threshold": &schema.Schema{ 124 Type: schema.TypeInt, 125 Required: true, 126 }, 127 128 "unhealthy_threshold": &schema.Schema{ 129 Type: schema.TypeInt, 130 Required: true, 131 }, 132 133 "target": &schema.Schema{ 134 Type: schema.TypeString, 135 Required: true, 136 }, 137 138 "interval": &schema.Schema{ 139 Type: schema.TypeInt, 140 Required: true, 141 }, 142 143 "timeout": &schema.Schema{ 144 Type: schema.TypeInt, 145 Required: true, 146 }, 147 }, 148 }, 149 Set: resourceAwsElbHealthCheckHash, 150 }, 151 152 "dns_name": &schema.Schema{ 153 Type: schema.TypeString, 154 Computed: true, 155 }, 156 }, 157 } 158 } 159 160 func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { 161 elbconn := meta.(*AWSClient).elbconn 162 163 // Expand the "listener" set to goamz compat []elb.Listener 164 listeners, err := expandListeners(d.Get("listener").(*schema.Set).List()) 165 if err != nil { 166 return err 167 } 168 169 // Provision the elb 170 elbOpts := &elb.CreateLoadBalancer{ 171 LoadBalancerName: d.Get("name").(string), 172 Listeners: listeners, 173 Internal: d.Get("internal").(bool), 174 } 175 176 if v, ok := d.GetOk("availability_zones"); ok { 177 elbOpts.AvailZone = expandStringList(v.(*schema.Set).List()) 178 } 179 180 if v, ok := d.GetOk("security_groups"); ok { 181 elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List()) 182 } 183 184 if v, ok := d.GetOk("subnets"); ok { 185 elbOpts.Subnets = expandStringList(v.(*schema.Set).List()) 186 } 187 188 log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts) 189 if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil { 190 return fmt.Errorf("Error creating ELB: %s", err) 191 } 192 193 // Assign the elb's unique identifier for use later 194 d.SetId(d.Get("name").(string)) 195 log.Printf("[INFO] ELB ID: %s", d.Id()) 196 197 // Enable partial mode and record what we set 198 d.Partial(true) 199 d.SetPartial("name") 200 d.SetPartial("internal") 201 d.SetPartial("availability_zones") 202 d.SetPartial("listener") 203 d.SetPartial("security_groups") 204 d.SetPartial("subnets") 205 206 if d.HasChange("health_check") { 207 vs := d.Get("health_check").(*schema.Set).List() 208 if len(vs) > 0 { 209 check := vs[0].(map[string]interface{}) 210 211 configureHealthCheckOpts := elb.ConfigureHealthCheck{ 212 LoadBalancerName: d.Id(), 213 Check: elb.HealthCheck{ 214 HealthyThreshold: int64(check["healthy_threshold"].(int)), 215 UnhealthyThreshold: int64(check["unhealthy_threshold"].(int)), 216 Interval: int64(check["interval"].(int)), 217 Target: check["target"].(string), 218 Timeout: int64(check["timeout"].(int)), 219 }, 220 } 221 222 _, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 223 if err != nil { 224 return fmt.Errorf("Failure configuring health check: %s", err) 225 } 226 } 227 } 228 229 return resourceAwsElbUpdate(d, meta) 230 } 231 232 func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { 233 elbconn := meta.(*AWSClient).elbconn 234 235 // Retrieve the ELB properties for updating the state 236 describeElbOpts := &elb.DescribeLoadBalancer{ 237 Names: []string{d.Id()}, 238 } 239 240 describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) 241 if err != nil { 242 if ec2err, ok := err.(*elb.Error); ok && ec2err.Code == "LoadBalancerNotFound" { 243 // The ELB is gone now, so just remove it from the state 244 d.SetId("") 245 return nil 246 } 247 248 return fmt.Errorf("Error retrieving ELB: %s", err) 249 } 250 if len(describeResp.LoadBalancers) != 1 { 251 return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers) 252 } 253 254 lb := describeResp.LoadBalancers[0] 255 256 d.Set("name", lb.LoadBalancerName) 257 d.Set("dns_name", lb.DNSName) 258 d.Set("internal", lb.Scheme == "internal") 259 d.Set("availability_zones", lb.AvailabilityZones) 260 d.Set("instances", flattenInstances(lb.Instances)) 261 d.Set("listener", flattenListeners(lb.Listeners)) 262 d.Set("security_groups", lb.SecurityGroups) 263 d.Set("subnets", lb.Subnets) 264 265 // There's only one health check, so save that to state as we 266 // currently can 267 if lb.HealthCheck.Target != "" { 268 d.Set("health_check", flattenHealthCheck(lb.HealthCheck)) 269 } 270 271 return nil 272 } 273 274 func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { 275 elbconn := meta.(*AWSClient).elbconn 276 277 d.Partial(true) 278 279 // If we currently have instances, or did have instances, 280 // we want to figure out what to add and remove from the load 281 // balancer 282 if d.HasChange("instances") { 283 o, n := d.GetChange("instances") 284 os := o.(*schema.Set) 285 ns := n.(*schema.Set) 286 remove := expandStringList(os.Difference(ns).List()) 287 add := expandStringList(ns.Difference(os).List()) 288 289 if len(add) > 0 { 290 registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{ 291 LoadBalancerName: d.Id(), 292 Instances: add, 293 } 294 295 _, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts) 296 if err != nil { 297 return fmt.Errorf("Failure registering instances: %s", err) 298 } 299 } 300 if len(remove) > 0 { 301 deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancer{ 302 LoadBalancerName: d.Id(), 303 Instances: remove, 304 } 305 306 _, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts) 307 if err != nil { 308 return fmt.Errorf("Failure deregistering instances: %s", err) 309 } 310 } 311 312 d.SetPartial("instances") 313 } 314 315 log.Println("[INFO] outside modify attributes") 316 if d.HasChange("cross_zone_load_balancing") { 317 log.Println("[INFO] inside modify attributes") 318 attrs := elb.ModifyLoadBalancerAttributes{ 319 LoadBalancerName: d.Get("name").(string), 320 LoadBalancerAttributes: elb.LoadBalancerAttributes{ 321 CrossZoneLoadBalancingEnabled: d.Get("cross_zone_load_balancing").(bool), 322 }, 323 } 324 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 325 if err != nil { 326 return fmt.Errorf("Failure configuring cross zone balancing: %s", err) 327 } 328 d.SetPartial("cross_zone_load_balancing") 329 } 330 331 if d.HasChange("health_check") { 332 vs := d.Get("health_check").(*schema.Set).List() 333 if len(vs) > 0 { 334 check := vs[0].(map[string]interface{}) 335 configureHealthCheckOpts := elb.ConfigureHealthCheck{ 336 LoadBalancerName: d.Id(), 337 Check: elb.HealthCheck{ 338 HealthyThreshold: int64(check["healthy_threshold"].(int)), 339 UnhealthyThreshold: int64(check["unhealthy_threshold"].(int)), 340 Interval: int64(check["interval"].(int)), 341 Target: check["target"].(string), 342 Timeout: int64(check["timeout"].(int)), 343 }, 344 } 345 _, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 346 if err != nil { 347 return fmt.Errorf("Failure configuring health check: %s", err) 348 } 349 d.SetPartial("health_check") 350 } 351 } 352 353 d.Partial(false) 354 return resourceAwsElbRead(d, meta) 355 } 356 357 func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { 358 elbconn := meta.(*AWSClient).elbconn 359 360 log.Printf("[INFO] Deleting ELB: %s", d.Id()) 361 362 // Destroy the load balancer 363 deleteElbOpts := elb.DeleteLoadBalancer{ 364 LoadBalancerName: d.Id(), 365 } 366 if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 367 return fmt.Errorf("Error deleting ELB: %s", err) 368 } 369 370 return nil 371 } 372 373 func resourceAwsElbHealthCheckHash(v interface{}) int { 374 var buf bytes.Buffer 375 m := v.(map[string]interface{}) 376 buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) 377 buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) 378 buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) 379 buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) 380 buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) 381 382 return hashcode.String(buf.String()) 383 } 384 385 func resourceAwsElbListenerHash(v interface{}) int { 386 var buf bytes.Buffer 387 m := v.(map[string]interface{}) 388 buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) 389 buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string))) 390 buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) 391 buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string))) 392 393 if v, ok := m["ssl_certificate_id"]; ok { 394 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 395 } 396 397 return hashcode.String(buf.String()) 398 }