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