github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/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/aws-sdk-go/aws" 9 "github.com/hashicorp/aws-sdk-go/gen/elb" 10 "github.com/hashicorp/terraform/helper/hashcode" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsElb() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsElbCreate, 17 Read: resourceAwsElbRead, 18 Update: resourceAwsElbUpdate, 19 Delete: resourceAwsElbDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "name": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 28 "internal": &schema.Schema{ 29 Type: schema.TypeBool, 30 Optional: true, 31 ForceNew: true, 32 Computed: true, 33 }, 34 35 "cross_zone_load_balancing": &schema.Schema{ 36 Type: schema.TypeBool, 37 Optional: true, 38 }, 39 40 "availability_zones": &schema.Schema{ 41 Type: schema.TypeSet, 42 Elem: &schema.Schema{Type: schema.TypeString}, 43 Optional: true, 44 ForceNew: true, 45 Computed: true, 46 Set: func(v interface{}) int { 47 return hashcode.String(v.(string)) 48 }, 49 }, 50 51 "instances": &schema.Schema{ 52 Type: schema.TypeSet, 53 Elem: &schema.Schema{Type: schema.TypeString}, 54 Optional: true, 55 Computed: true, 56 Set: func(v interface{}) int { 57 return hashcode.String(v.(string)) 58 }, 59 }, 60 61 // TODO: could be not ForceNew 62 "security_groups": &schema.Schema{ 63 Type: schema.TypeSet, 64 Elem: &schema.Schema{Type: schema.TypeString}, 65 Optional: true, 66 ForceNew: true, 67 Computed: true, 68 Set: func(v interface{}) int { 69 return hashcode.String(v.(string)) 70 }, 71 }, 72 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 "listener": &schema.Schema{ 85 Type: schema.TypeSet, 86 Required: true, 87 Elem: &schema.Resource{ 88 Schema: map[string]*schema.Schema{ 89 "instance_port": &schema.Schema{ 90 Type: schema.TypeInt, 91 Required: true, 92 }, 93 94 "instance_protocol": &schema.Schema{ 95 Type: schema.TypeString, 96 Required: true, 97 }, 98 99 "lb_port": &schema.Schema{ 100 Type: schema.TypeInt, 101 Required: true, 102 }, 103 104 "lb_protocol": &schema.Schema{ 105 Type: schema.TypeString, 106 Required: true, 107 }, 108 109 "ssl_certificate_id": &schema.Schema{ 110 Type: schema.TypeString, 111 Optional: true, 112 }, 113 }, 114 }, 115 Set: resourceAwsElbListenerHash, 116 }, 117 118 "health_check": &schema.Schema{ 119 Type: schema.TypeSet, 120 Optional: true, 121 Computed: true, 122 Elem: &schema.Resource{ 123 Schema: map[string]*schema.Schema{ 124 "healthy_threshold": &schema.Schema{ 125 Type: schema.TypeInt, 126 Required: true, 127 }, 128 129 "unhealthy_threshold": &schema.Schema{ 130 Type: schema.TypeInt, 131 Required: true, 132 }, 133 134 "target": &schema.Schema{ 135 Type: schema.TypeString, 136 Required: true, 137 }, 138 139 "interval": &schema.Schema{ 140 Type: schema.TypeInt, 141 Required: true, 142 }, 143 144 "timeout": &schema.Schema{ 145 Type: schema.TypeInt, 146 Required: true, 147 }, 148 }, 149 }, 150 Set: resourceAwsElbHealthCheckHash, 151 }, 152 153 "dns_name": &schema.Schema{ 154 Type: schema.TypeString, 155 Computed: true, 156 }, 157 }, 158 } 159 } 160 161 func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { 162 elbconn := meta.(*AWSClient).elbconn 163 164 // Expand the "listener" set to aws-sdk-go compat []elb.Listener 165 listeners, err := expandListeners(d.Get("listener").(*schema.Set).List()) 166 if err != nil { 167 return err 168 } 169 170 // Provision the elb 171 172 elbOpts := &elb.CreateAccessPointInput{ 173 LoadBalancerName: aws.String(d.Get("name").(string)), 174 Listeners: listeners, 175 } 176 177 if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) { 178 elbOpts.Scheme = aws.String("internal") 179 } 180 181 if v, ok := d.GetOk("availability_zones"); ok { 182 elbOpts.AvailabilityZones = 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.ConfigureHealthCheckInput{ 217 LoadBalancerName: aws.String(d.Id()), 218 HealthCheck: &elb.HealthCheck{ 219 HealthyThreshold: aws.Integer(check["healthy_threshold"].(int)), 220 UnhealthyThreshold: aws.Integer(check["unhealthy_threshold"].(int)), 221 Interval: aws.Integer(check["interval"].(int)), 222 Target: aws.String(check["target"].(string)), 223 Timeout: aws.Integer(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.DescribeAccessPointsInput{ 242 LoadBalancerNames: []string{d.Id()}, 243 } 244 245 describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts) 246 if err != nil { 247 if ec2err, ok := err.(aws.APIError); 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.LoadBalancerDescriptions) != 1 { 256 return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) 257 } 258 259 lb := describeResp.LoadBalancerDescriptions[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.ListenerDescriptions)) 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 := expandInstanceString(os.Difference(ns).List()) 292 add := expandInstanceString(ns.Difference(os).List()) 293 294 if len(add) > 0 { 295 registerInstancesOpts := elb.RegisterEndPointsInput{ 296 LoadBalancerName: aws.String(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.DeregisterEndPointsInput{ 307 LoadBalancerName: aws.String(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.ModifyLoadBalancerAttributesInput{ 324 LoadBalancerName: aws.String(d.Get("name").(string)), 325 LoadBalancerAttributes: &elb.LoadBalancerAttributes{ 326 CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{ 327 aws.Boolean(d.Get("cross_zone_load_balancing").(bool)), 328 }, 329 }, 330 } 331 _, err := elbconn.ModifyLoadBalancerAttributes(&attrs) 332 if err != nil { 333 return fmt.Errorf("Failure configuring cross zone balancing: %s", err) 334 } 335 d.SetPartial("cross_zone_load_balancing") 336 } 337 338 if d.HasChange("health_check") { 339 vs := d.Get("health_check").(*schema.Set).List() 340 if len(vs) > 0 { 341 check := vs[0].(map[string]interface{}) 342 configureHealthCheckOpts := elb.ConfigureHealthCheckInput{ 343 LoadBalancerName: aws.String(d.Id()), 344 HealthCheck: &elb.HealthCheck{ 345 HealthyThreshold: aws.Integer(check["healthy_threshold"].(int)), 346 UnhealthyThreshold: aws.Integer(check["unhealthy_threshold"].(int)), 347 Interval: aws.Integer(check["interval"].(int)), 348 Target: aws.String(check["target"].(string)), 349 Timeout: aws.Integer(check["timeout"].(int)), 350 }, 351 } 352 _, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts) 353 if err != nil { 354 return fmt.Errorf("Failure configuring health check: %s", err) 355 } 356 d.SetPartial("health_check") 357 } 358 } 359 360 d.Partial(false) 361 362 return resourceAwsElbRead(d, meta) 363 } 364 365 func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error { 366 elbconn := meta.(*AWSClient).elbconn 367 368 log.Printf("[INFO] Deleting ELB: %s", d.Id()) 369 370 // Destroy the load balancer 371 deleteElbOpts := elb.DeleteAccessPointInput{ 372 LoadBalancerName: aws.String(d.Id()), 373 } 374 if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil { 375 return fmt.Errorf("Error deleting ELB: %s", err) 376 } 377 378 return nil 379 } 380 381 func resourceAwsElbHealthCheckHash(v interface{}) int { 382 var buf bytes.Buffer 383 m := v.(map[string]interface{}) 384 buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int))) 385 buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int))) 386 buf.WriteString(fmt.Sprintf("%s-", m["target"].(string))) 387 buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int))) 388 buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int))) 389 390 return hashcode.String(buf.String()) 391 } 392 393 func resourceAwsElbListenerHash(v interface{}) int { 394 var buf bytes.Buffer 395 m := v.(map[string]interface{}) 396 buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int))) 397 buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string))) 398 buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int))) 399 buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string))) 400 401 if v, ok := m["ssl_certificate_id"]; ok { 402 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 403 } 404 405 return hashcode.String(buf.String()) 406 }