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