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