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