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