github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_elb.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/awslabs/aws-sdk-go/aws"
     9  	"github.com/awslabs/aws-sdk-go/service/elb"
    10  	"github.com/hashicorp/terraform/helper/hashcode"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func resourceAwsElb() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceAwsElbCreate,
    17  		Read:   resourceAwsElbRead,
    18  		Update: resourceAwsElbUpdate,
    19  		Delete: resourceAwsElbDelete,
    20  
    21  		Schema: map[string]*schema.Schema{
    22  			"name": &schema.Schema{
    23  				Type:     schema.TypeString,
    24  				Required: true,
    25  				ForceNew: true,
    26  			},
    27  
    28  			"internal": &schema.Schema{
    29  				Type:     schema.TypeBool,
    30  				Optional: true,
    31  				ForceNew: true,
    32  				Computed: true,
    33  			},
    34  
    35  			"cross_zone_load_balancing": &schema.Schema{
    36  				Type:     schema.TypeBool,
    37  				Optional: true,
    38  			},
    39  
    40  			"availability_zones": &schema.Schema{
    41  				Type:     schema.TypeSet,
    42  				Elem:     &schema.Schema{Type: schema.TypeString},
    43  				Optional: true,
    44  				ForceNew: true,
    45  				Computed: true,
    46  				Set: func(v interface{}) int {
    47  					return hashcode.String(v.(string))
    48  				},
    49  			},
    50  
    51  			"instances": &schema.Schema{
    52  				Type:     schema.TypeSet,
    53  				Elem:     &schema.Schema{Type: schema.TypeString},
    54  				Optional: true,
    55  				Computed: true,
    56  				Set: func(v interface{}) int {
    57  					return hashcode.String(v.(string))
    58  				},
    59  			},
    60  
    61  			// TODO: could be not ForceNew
    62  			"security_groups": &schema.Schema{
    63  				Type:     schema.TypeSet,
    64  				Elem:     &schema.Schema{Type: schema.TypeString},
    65  				Optional: true,
    66  				ForceNew: true,
    67  				Computed: true,
    68  				Set: func(v interface{}) int {
    69  					return hashcode.String(v.(string))
    70  				},
    71  			},
    72  
    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  			"listener": &schema.Schema{
    85  				Type:     schema.TypeSet,
    86  				Required: true,
    87  				Elem: &schema.Resource{
    88  					Schema: map[string]*schema.Schema{
    89  						"instance_port": &schema.Schema{
    90  							Type:     schema.TypeInt,
    91  							Required: true,
    92  						},
    93  
    94  						"instance_protocol": &schema.Schema{
    95  							Type:     schema.TypeString,
    96  							Required: true,
    97  						},
    98  
    99  						"lb_port": &schema.Schema{
   100  							Type:     schema.TypeInt,
   101  							Required: true,
   102  						},
   103  
   104  						"lb_protocol": &schema.Schema{
   105  							Type:     schema.TypeString,
   106  							Required: true,
   107  						},
   108  
   109  						"ssl_certificate_id": &schema.Schema{
   110  							Type:     schema.TypeString,
   111  							Optional: true,
   112  						},
   113  					},
   114  				},
   115  				Set: resourceAwsElbListenerHash,
   116  			},
   117  
   118  			"health_check": &schema.Schema{
   119  				Type:     schema.TypeSet,
   120  				Optional: true,
   121  				Computed: true,
   122  				Elem: &schema.Resource{
   123  					Schema: map[string]*schema.Schema{
   124  						"healthy_threshold": &schema.Schema{
   125  							Type:     schema.TypeInt,
   126  							Required: true,
   127  						},
   128  
   129  						"unhealthy_threshold": &schema.Schema{
   130  							Type:     schema.TypeInt,
   131  							Required: true,
   132  						},
   133  
   134  						"target": &schema.Schema{
   135  							Type:     schema.TypeString,
   136  							Required: true,
   137  						},
   138  
   139  						"interval": &schema.Schema{
   140  							Type:     schema.TypeInt,
   141  							Required: true,
   142  						},
   143  
   144  						"timeout": &schema.Schema{
   145  							Type:     schema.TypeInt,
   146  							Required: true,
   147  						},
   148  					},
   149  				},
   150  				Set: resourceAwsElbHealthCheckHash,
   151  			},
   152  
   153  			"dns_name": &schema.Schema{
   154  				Type:     schema.TypeString,
   155  				Computed: true,
   156  			},
   157  
   158  			"tags": tagsSchema(),
   159  		},
   160  	}
   161  }
   162  
   163  func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
   164  	elbconn := meta.(*AWSClient).elbconn
   165  
   166  	// Expand the "listener" set to aws-sdk-go compat []*elb.Listener
   167  	listeners, err := expandListenersSDK(d.Get("listener").(*schema.Set).List())
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	tags := tagsFromMapELB(d.Get("tags").(map[string]interface{}))
   173  	// Provision the elb
   174  	elbOpts := &elb.CreateLoadBalancerInput{
   175  		LoadBalancerName: aws.String(d.Get("name").(string)),
   176  		Listeners:        listeners,
   177  		Tags:             tags,
   178  	}
   179  
   180  	if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) {
   181  		elbOpts.Scheme = aws.String("internal")
   182  	}
   183  
   184  	if v, ok := d.GetOk("availability_zones"); ok {
   185  		elbOpts.AvailabilityZones = expandStringListSDK(v.(*schema.Set).List())
   186  	}
   187  
   188  	if v, ok := d.GetOk("security_groups"); ok {
   189  		elbOpts.SecurityGroups = expandStringListSDK(v.(*schema.Set).List())
   190  	}
   191  
   192  	if v, ok := d.GetOk("subnets"); ok {
   193  		elbOpts.Subnets = expandStringListSDK(v.(*schema.Set).List())
   194  	}
   195  
   196  	log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
   197  	if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil {
   198  		return fmt.Errorf("Error creating ELB: %s", err)
   199  	}
   200  
   201  	// Assign the elb's unique identifier for use later
   202  	d.SetId(d.Get("name").(string))
   203  	log.Printf("[INFO] ELB ID: %s", d.Id())
   204  
   205  	// Enable partial mode and record what we set
   206  	d.Partial(true)
   207  	d.SetPartial("name")
   208  	d.SetPartial("internal")
   209  	d.SetPartial("availability_zones")
   210  	d.SetPartial("listener")
   211  	d.SetPartial("security_groups")
   212  	d.SetPartial("subnets")
   213  
   214  	d.Set("tags", tagsToMapELB(tags))
   215  
   216  	if d.HasChange("health_check") {
   217  		vs := d.Get("health_check").(*schema.Set).List()
   218  		if len(vs) > 0 {
   219  			check := vs[0].(map[string]interface{})
   220  
   221  			configureHealthCheckOpts := elb.ConfigureHealthCheckInput{
   222  				LoadBalancerName: aws.String(d.Id()),
   223  				HealthCheck: &elb.HealthCheck{
   224  					HealthyThreshold:   aws.Long(int64(check["healthy_threshold"].(int))),
   225  					UnhealthyThreshold: aws.Long(int64(check["unhealthy_threshold"].(int))),
   226  					Interval:           aws.Long(int64(check["interval"].(int))),
   227  					Target:             aws.String(check["target"].(string)),
   228  					Timeout:            aws.Long(int64(check["timeout"].(int))),
   229  				},
   230  			}
   231  
   232  			_, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   233  			if err != nil {
   234  				return fmt.Errorf("Failure configuring health check: %s", err)
   235  			}
   236  		}
   237  	}
   238  
   239  	return resourceAwsElbUpdate(d, meta)
   240  }
   241  
   242  func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
   243  	elbconn := meta.(*AWSClient).elbconn
   244  
   245  	// Retrieve the ELB properties for updating the state
   246  	describeElbOpts := &elb.DescribeLoadBalancersInput{
   247  		LoadBalancerNames: []*string{aws.String(d.Id())},
   248  	}
   249  
   250  	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
   251  	if err != nil {
   252  		if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "LoadBalancerNotFound" {
   253  			// The ELB is gone now, so just remove it from the state
   254  			d.SetId("")
   255  			return nil
   256  		}
   257  
   258  		return fmt.Errorf("Error retrieving ELB: %s", err)
   259  	}
   260  	if len(describeResp.LoadBalancerDescriptions) != 1 {
   261  		return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions)
   262  	}
   263  
   264  	lb := describeResp.LoadBalancerDescriptions[0]
   265  
   266  	d.Set("name", *lb.LoadBalancerName)
   267  	d.Set("dns_name", *lb.DNSName)
   268  	d.Set("internal", *lb.Scheme == "internal")
   269  	d.Set("availability_zones", lb.AvailabilityZones)
   270  	d.Set("instances", flattenInstancesSDK(lb.Instances))
   271  	d.Set("listener", flattenListenersSDK(lb.ListenerDescriptions))
   272  	d.Set("security_groups", lb.SecurityGroups)
   273  	d.Set("subnets", lb.Subnets)
   274  
   275  	resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
   276  		LoadBalancerNames: []*string{lb.LoadBalancerName},
   277  	})
   278  
   279  	var et []*elb.Tag
   280  	if len(resp.TagDescriptions) > 0 {
   281  		et = resp.TagDescriptions[0].Tags
   282  	}
   283  	d.Set("tags", tagsToMapELB(et))
   284  	// There's only one health check, so save that to state as we
   285  	// currently can
   286  	if *lb.HealthCheck.Target != "" {
   287  		d.Set("health_check", flattenHealthCheckSDK(lb.HealthCheck))
   288  	}
   289  
   290  	return nil
   291  }
   292  
   293  func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
   294  	elbconn := meta.(*AWSClient).elbconn
   295  
   296  	d.Partial(true)
   297  
   298  	// If we currently have instances, or did have instances,
   299  	// we want to figure out what to add and remove from the load
   300  	// balancer
   301  	if d.HasChange("instances") {
   302  		o, n := d.GetChange("instances")
   303  		os := o.(*schema.Set)
   304  		ns := n.(*schema.Set)
   305  		remove := expandInstanceStringSDK(os.Difference(ns).List())
   306  		add := expandInstanceStringSDK(ns.Difference(os).List())
   307  
   308  		if len(add) > 0 {
   309  			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{
   310  				LoadBalancerName: aws.String(d.Id()),
   311  				Instances:        add,
   312  			}
   313  
   314  			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)
   315  			if err != nil {
   316  				return fmt.Errorf("Failure registering instances: %s", err)
   317  			}
   318  		}
   319  		if len(remove) > 0 {
   320  			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
   321  				LoadBalancerName: aws.String(d.Id()),
   322  				Instances:        remove,
   323  			}
   324  
   325  			_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
   326  			if err != nil {
   327  				return fmt.Errorf("Failure deregistering instances: %s", err)
   328  			}
   329  		}
   330  
   331  		d.SetPartial("instances")
   332  	}
   333  
   334  	log.Println("[INFO] outside modify attributes")
   335  	if d.HasChange("cross_zone_load_balancing") {
   336  		log.Println("[INFO] inside modify attributes")
   337  		attrs := elb.ModifyLoadBalancerAttributesInput{
   338  			LoadBalancerName: aws.String(d.Get("name").(string)),
   339  			LoadBalancerAttributes: &elb.LoadBalancerAttributes{
   340  				CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{
   341  					Enabled: aws.Boolean(d.Get("cross_zone_load_balancing").(bool)),
   342  				},
   343  			},
   344  		}
   345  		_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
   346  		if err != nil {
   347  			return fmt.Errorf("Failure configuring cross zone balancing: %s", err)
   348  		}
   349  		d.SetPartial("cross_zone_load_balancing")
   350  	}
   351  
   352  	if d.HasChange("health_check") {
   353  		vs := d.Get("health_check").(*schema.Set).List()
   354  		if len(vs) > 0 {
   355  			check := vs[0].(map[string]interface{})
   356  			configureHealthCheckOpts := elb.ConfigureHealthCheckInput{
   357  				LoadBalancerName: aws.String(d.Id()),
   358  				HealthCheck: &elb.HealthCheck{
   359  					HealthyThreshold:   aws.Long(int64(check["healthy_threshold"].(int))),
   360  					UnhealthyThreshold: aws.Long(int64(check["unhealthy_threshold"].(int))),
   361  					Interval:           aws.Long(int64(check["interval"].(int))),
   362  					Target:             aws.String(check["target"].(string)),
   363  					Timeout:            aws.Long(int64(check["timeout"].(int))),
   364  				},
   365  			}
   366  			_, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   367  			if err != nil {
   368  				return fmt.Errorf("Failure configuring health check: %s", err)
   369  			}
   370  			d.SetPartial("health_check")
   371  		}
   372  	}
   373  
   374  	if err := setTagsELB(elbconn, d); err != nil {
   375  		return err
   376  	} else {
   377  		d.SetPartial("tags")
   378  	}
   379  	d.Partial(false)
   380  
   381  	return resourceAwsElbRead(d, meta)
   382  }
   383  
   384  func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
   385  	elbconn := meta.(*AWSClient).elbconn
   386  
   387  	log.Printf("[INFO] Deleting ELB: %s", d.Id())
   388  
   389  	// Destroy the load balancer
   390  	deleteElbOpts := elb.DeleteLoadBalancerInput{
   391  		LoadBalancerName: aws.String(d.Id()),
   392  	}
   393  	if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
   394  		return fmt.Errorf("Error deleting ELB: %s", err)
   395  	}
   396  
   397  	return nil
   398  }
   399  
   400  func resourceAwsElbHealthCheckHash(v interface{}) int {
   401  	var buf bytes.Buffer
   402  	m := v.(map[string]interface{})
   403  	buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int)))
   404  	buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int)))
   405  	buf.WriteString(fmt.Sprintf("%s-", m["target"].(string)))
   406  	buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
   407  	buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int)))
   408  
   409  	return hashcode.String(buf.String())
   410  }
   411  
   412  func resourceAwsElbListenerHash(v interface{}) int {
   413  	var buf bytes.Buffer
   414  	m := v.(map[string]interface{})
   415  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   416  	buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string)))
   417  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   418  	buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string)))
   419  
   420  	if v, ok := m["ssl_certificate_id"]; ok {
   421  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   422  	}
   423  
   424  	return hashcode.String(buf.String())
   425  }