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