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