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