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(&registerInstancesOpts)
   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  }