github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/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/aws-sdk-go/aws"
     9  	"github.com/hashicorp/aws-sdk-go/gen/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  	}
   159  }
   160  
   161  func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
   162  	elbconn := meta.(*AWSClient).elbconn
   163  
   164  	// Expand the "listener" set to aws-sdk-go compat []elb.Listener
   165  	listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	// Provision the elb
   171  
   172  	elbOpts := &elb.CreateAccessPointInput{
   173  		LoadBalancerName: aws.String(d.Get("name").(string)),
   174  		Listeners:        listeners,
   175  	}
   176  
   177  	if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) {
   178  		elbOpts.Scheme = aws.String("internal")
   179  	}
   180  
   181  	if v, ok := d.GetOk("availability_zones"); ok {
   182  		elbOpts.AvailabilityZones = 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.ConfigureHealthCheckInput{
   217  				LoadBalancerName: aws.String(d.Id()),
   218  				HealthCheck: &elb.HealthCheck{
   219  					HealthyThreshold:   aws.Integer(check["healthy_threshold"].(int)),
   220  					UnhealthyThreshold: aws.Integer(check["unhealthy_threshold"].(int)),
   221  					Interval:           aws.Integer(check["interval"].(int)),
   222  					Target:             aws.String(check["target"].(string)),
   223  					Timeout:            aws.Integer(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.DescribeAccessPointsInput{
   242  		LoadBalancerNames: []string{d.Id()},
   243  	}
   244  
   245  	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
   246  	if err != nil {
   247  		if ec2err, ok := err.(aws.APIError); 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.LoadBalancerDescriptions) != 1 {
   256  		return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions)
   257  	}
   258  
   259  	lb := describeResp.LoadBalancerDescriptions[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.ListenerDescriptions))
   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 := expandInstanceString(os.Difference(ns).List())
   292  		add := expandInstanceString(ns.Difference(os).List())
   293  
   294  		if len(add) > 0 {
   295  			registerInstancesOpts := elb.RegisterEndPointsInput{
   296  				LoadBalancerName: aws.String(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.DeregisterEndPointsInput{
   307  				LoadBalancerName: aws.String(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.ModifyLoadBalancerAttributesInput{
   324  			LoadBalancerName: aws.String(d.Get("name").(string)),
   325  			LoadBalancerAttributes: &elb.LoadBalancerAttributes{
   326  				CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{
   327  					aws.Boolean(d.Get("cross_zone_load_balancing").(bool)),
   328  				},
   329  			},
   330  		}
   331  		_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
   332  		if err != nil {
   333  			return fmt.Errorf("Failure configuring cross zone balancing: %s", err)
   334  		}
   335  		d.SetPartial("cross_zone_load_balancing")
   336  	}
   337  
   338  	if d.HasChange("health_check") {
   339  		vs := d.Get("health_check").(*schema.Set).List()
   340  		if len(vs) > 0 {
   341  			check := vs[0].(map[string]interface{})
   342  			configureHealthCheckOpts := elb.ConfigureHealthCheckInput{
   343  				LoadBalancerName: aws.String(d.Id()),
   344  				HealthCheck: &elb.HealthCheck{
   345  					HealthyThreshold:   aws.Integer(check["healthy_threshold"].(int)),
   346  					UnhealthyThreshold: aws.Integer(check["unhealthy_threshold"].(int)),
   347  					Interval:           aws.Integer(check["interval"].(int)),
   348  					Target:             aws.String(check["target"].(string)),
   349  					Timeout:            aws.Integer(check["timeout"].(int)),
   350  				},
   351  			}
   352  			_, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   353  			if err != nil {
   354  				return fmt.Errorf("Failure configuring health check: %s", err)
   355  			}
   356  			d.SetPartial("health_check")
   357  		}
   358  	}
   359  
   360  	d.Partial(false)
   361  
   362  	return resourceAwsElbRead(d, meta)
   363  }
   364  
   365  func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
   366  	elbconn := meta.(*AWSClient).elbconn
   367  
   368  	log.Printf("[INFO] Deleting ELB: %s", d.Id())
   369  
   370  	// Destroy the load balancer
   371  	deleteElbOpts := elb.DeleteAccessPointInput{
   372  		LoadBalancerName: aws.String(d.Id()),
   373  	}
   374  	if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
   375  		return fmt.Errorf("Error deleting ELB: %s", err)
   376  	}
   377  
   378  	return nil
   379  }
   380  
   381  func resourceAwsElbHealthCheckHash(v interface{}) int {
   382  	var buf bytes.Buffer
   383  	m := v.(map[string]interface{})
   384  	buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int)))
   385  	buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int)))
   386  	buf.WriteString(fmt.Sprintf("%s-", m["target"].(string)))
   387  	buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
   388  	buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int)))
   389  
   390  	return hashcode.String(buf.String())
   391  }
   392  
   393  func resourceAwsElbListenerHash(v interface{}) int {
   394  	var buf bytes.Buffer
   395  	m := v.(map[string]interface{})
   396  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   397  	buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string)))
   398  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   399  	buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string)))
   400  
   401  	if v, ok := m["ssl_certificate_id"]; ok {
   402  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   403  	}
   404  
   405  	return hashcode.String(buf.String())
   406  }