github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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  			// TODO: could be not ForceNew
    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  			// TODO: could be not ForceNew
    85  			"listener": &schema.Schema{
    86  				Type:     schema.TypeSet,
    87  				Required: true,
    88  				ForceNew: true,
    89  				Elem: &schema.Resource{
    90  					Schema: map[string]*schema.Schema{
    91  						"instance_port": &schema.Schema{
    92  							Type:     schema.TypeInt,
    93  							Required: true,
    94  						},
    95  
    96  						"instance_protocol": &schema.Schema{
    97  							Type:     schema.TypeString,
    98  							Required: true,
    99  						},
   100  
   101  						"lb_port": &schema.Schema{
   102  							Type:     schema.TypeInt,
   103  							Required: true,
   104  						},
   105  
   106  						"lb_protocol": &schema.Schema{
   107  							Type:     schema.TypeString,
   108  							Required: true,
   109  						},
   110  
   111  						"ssl_certificate_id": &schema.Schema{
   112  							Type:     schema.TypeString,
   113  							Optional: true,
   114  						},
   115  					},
   116  				},
   117  				Set: resourceAwsElbListenerHash,
   118  			},
   119  
   120  			// TODO: could be not ForceNew
   121  			"health_check": &schema.Schema{
   122  				Type:     schema.TypeSet,
   123  				Optional: true,
   124  				ForceNew: true,
   125  				Computed: true,
   126  				Elem: &schema.Resource{
   127  					Schema: map[string]*schema.Schema{
   128  						"healthy_threshold": &schema.Schema{
   129  							Type:     schema.TypeInt,
   130  							Required: true,
   131  						},
   132  
   133  						"unhealthy_threshold": &schema.Schema{
   134  							Type:     schema.TypeInt,
   135  							Required: true,
   136  						},
   137  
   138  						"target": &schema.Schema{
   139  							Type:     schema.TypeString,
   140  							Required: true,
   141  						},
   142  
   143  						"interval": &schema.Schema{
   144  							Type:     schema.TypeInt,
   145  							Required: true,
   146  						},
   147  
   148  						"timeout": &schema.Schema{
   149  							Type:     schema.TypeInt,
   150  							Required: true,
   151  						},
   152  					},
   153  				},
   154  				Set: resourceAwsElbHealthCheckHash,
   155  			},
   156  
   157  			"dns_name": &schema.Schema{
   158  				Type:     schema.TypeString,
   159  				Computed: true,
   160  			},
   161  		},
   162  	}
   163  }
   164  
   165  func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
   166  	elbconn := meta.(*AWSClient).elbconn
   167  
   168  	// Expand the "listener" set to goamz compat []elb.Listener
   169  	listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	// Provision the elb
   175  	elbOpts := &elb.CreateLoadBalancer{
   176  		LoadBalancerName: d.Get("name").(string),
   177  		Listeners:        listeners,
   178  		Internal:         d.Get("internal").(bool),
   179  	}
   180  
   181  	if v, ok := d.GetOk("availability_zones"); ok {
   182  		elbOpts.AvailZone = 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.ConfigureHealthCheck{
   217  				LoadBalancerName: d.Id(),
   218  				Check: elb.HealthCheck{
   219  					HealthyThreshold:   int64(check["healthy_threshold"].(int)),
   220  					UnhealthyThreshold: int64(check["unhealthy_threshold"].(int)),
   221  					Interval:           int64(check["interval"].(int)),
   222  					Target:             check["target"].(string),
   223  					Timeout:            int64(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.DescribeLoadBalancer{
   242  		Names: []string{d.Id()},
   243  	}
   244  
   245  	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
   246  	if err != nil {
   247  		if ec2err, ok := err.(*elb.Error); 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.LoadBalancers) != 1 {
   256  		return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers)
   257  	}
   258  
   259  	lb := describeResp.LoadBalancers[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.Listeners))
   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 := expandStringList(os.Difference(ns).List())
   292  		add := expandStringList(ns.Difference(os).List())
   293  
   294  		if len(add) > 0 {
   295  			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
   296  				LoadBalancerName: d.Id(),
   297  				Instances:        add,
   298  			}
   299  
   300  			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)
   301  			if err != nil {
   302  				return fmt.Errorf("Failure registering instances: %s", err)
   303  			}
   304  		}
   305  		if len(remove) > 0 {
   306  			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancer{
   307  				LoadBalancerName: 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.ModifyLoadBalancerAttributes{
   324  			LoadBalancerName: d.Get("name").(string),
   325  			LoadBalancerAttributes: elb.LoadBalancerAttributes{
   326  				CrossZoneLoadBalancingEnabled: d.Get("cross_zone_load_balancing").(bool),
   327  			},
   328  		}
   329  		_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
   330  		if err != nil {
   331  			return fmt.Errorf("Failure configuring health check: %s", err)
   332  		}
   333  		d.SetPartial("cross_zone_load_balancing")
   334  	}
   335  
   336  	d.Partial(false)
   337  	return resourceAwsElbRead(d, meta)
   338  }
   339  
   340  func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
   341  	elbconn := meta.(*AWSClient).elbconn
   342  
   343  	log.Printf("[INFO] Deleting ELB: %s", d.Id())
   344  
   345  	// Destroy the load balancer
   346  	deleteElbOpts := elb.DeleteLoadBalancer{
   347  		LoadBalancerName: d.Id(),
   348  	}
   349  	if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
   350  		return fmt.Errorf("Error deleting ELB: %s", err)
   351  	}
   352  
   353  	return nil
   354  }
   355  
   356  func resourceAwsElbHealthCheckHash(v interface{}) int {
   357  	var buf bytes.Buffer
   358  	m := v.(map[string]interface{})
   359  	buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int)))
   360  	buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int)))
   361  	buf.WriteString(fmt.Sprintf("%s-", m["target"].(string)))
   362  	buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
   363  	buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int)))
   364  
   365  	return hashcode.String(buf.String())
   366  }
   367  
   368  func resourceAwsElbListenerHash(v interface{}) int {
   369  	var buf bytes.Buffer
   370  	m := v.(map[string]interface{})
   371  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   372  	buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string)))
   373  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   374  	buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string)))
   375  
   376  	if v, ok := m["ssl_certificate_id"]; ok {
   377  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   378  	}
   379  
   380  	return hashcode.String(buf.String())
   381  }