github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/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  			"cross_zone_load_balancing": &schema.Schema{
    35  				Type:     schema.TypeBool,
    36  				Optional: true,
    37  			},
    38  
    39  			"availability_zones": &schema.Schema{
    40  				Type:     schema.TypeSet,
    41  				Elem:     &schema.Schema{Type: schema.TypeString},
    42  				Optional: true,
    43  				ForceNew: true,
    44  				Computed: true,
    45  				Set: func(v interface{}) int {
    46  					return hashcode.String(v.(string))
    47  				},
    48  			},
    49  
    50  			"instances": &schema.Schema{
    51  				Type:     schema.TypeSet,
    52  				Elem:     &schema.Schema{Type: schema.TypeString},
    53  				Optional: true,
    54  				Computed: true,
    55  				Set: func(v interface{}) int {
    56  					return hashcode.String(v.(string))
    57  				},
    58  			},
    59  
    60  			// TODO: could be not ForceNew
    61  			"security_groups": &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  			"subnets": &schema.Schema{
    73  				Type:     schema.TypeSet,
    74  				Elem:     &schema.Schema{Type: schema.TypeString},
    75  				Optional: true,
    76  				Computed: true,
    77  				Set: func(v interface{}) int {
    78  					return hashcode.String(v.(string))
    79  				},
    80  			},
    81  
    82  			// TODO: could be not ForceNew
    83  			"listener": &schema.Schema{
    84  				Type:     schema.TypeSet,
    85  				Required: true,
    86  				ForceNew: 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  			// TODO: could be not ForceNew
   119  			"health_check": &schema.Schema{
   120  				Type:     schema.TypeSet,
   121  				Optional: true,
   122  				Computed: true,
   123  				Elem: &schema.Resource{
   124  					Schema: map[string]*schema.Schema{
   125  						"healthy_threshold": &schema.Schema{
   126  							Type:     schema.TypeInt,
   127  							Required: true,
   128  						},
   129  
   130  						"unhealthy_threshold": &schema.Schema{
   131  							Type:     schema.TypeInt,
   132  							Required: true,
   133  						},
   134  
   135  						"target": &schema.Schema{
   136  							Type:     schema.TypeString,
   137  							Required: true,
   138  						},
   139  
   140  						"interval": &schema.Schema{
   141  							Type:     schema.TypeInt,
   142  							Required: true,
   143  						},
   144  
   145  						"timeout": &schema.Schema{
   146  							Type:     schema.TypeInt,
   147  							Required: true,
   148  						},
   149  					},
   150  				},
   151  				Set: resourceAwsElbHealthCheckHash,
   152  			},
   153  
   154  			"dns_name": &schema.Schema{
   155  				Type:     schema.TypeString,
   156  				Computed: true,
   157  			},
   158  		},
   159  	}
   160  }
   161  
   162  func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
   163  	elbconn := meta.(*AWSClient).elbconn
   164  
   165  	// Expand the "listener" set to goamz compat []elb.Listener
   166  	listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	// Provision the elb
   172  	elbOpts := &elb.CreateLoadBalancer{
   173  		LoadBalancerName: d.Get("name").(string),
   174  		Listeners:        listeners,
   175  		Internal:         d.Get("internal").(bool),
   176  	}
   177  
   178  	if v, ok := d.GetOk("availability_zones"); ok {
   179  		elbOpts.AvailZone = expandStringList(v.(*schema.Set).List())
   180  	}
   181  
   182  	if v, ok := d.GetOk("security_groups"); ok {
   183  		elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List())
   184  	}
   185  
   186  	if v, ok := d.GetOk("subnets"); ok {
   187  		elbOpts.Subnets = expandStringList(v.(*schema.Set).List())
   188  	}
   189  
   190  	log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
   191  	if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil {
   192  		return fmt.Errorf("Error creating ELB: %s", err)
   193  	}
   194  
   195  	// Assign the elb's unique identifier for use later
   196  	d.SetId(d.Get("name").(string))
   197  	log.Printf("[INFO] ELB ID: %s", d.Id())
   198  
   199  	// Enable partial mode and record what we set
   200  	d.Partial(true)
   201  	d.SetPartial("name")
   202  	d.SetPartial("internal")
   203  	d.SetPartial("availability_zones")
   204  	d.SetPartial("listener")
   205  	d.SetPartial("security_groups")
   206  	d.SetPartial("subnets")
   207  
   208  	if d.HasChange("health_check") {
   209  		vs := d.Get("health_check").(*schema.Set).List()
   210  		if len(vs) > 0 {
   211  			check := vs[0].(map[string]interface{})
   212  
   213  			configureHealthCheckOpts := elb.ConfigureHealthCheck{
   214  				LoadBalancerName: d.Id(),
   215  				Check: elb.HealthCheck{
   216  					HealthyThreshold:   int64(check["healthy_threshold"].(int)),
   217  					UnhealthyThreshold: int64(check["unhealthy_threshold"].(int)),
   218  					Interval:           int64(check["interval"].(int)),
   219  					Target:             check["target"].(string),
   220  					Timeout:            int64(check["timeout"].(int)),
   221  				},
   222  			}
   223  
   224  			_, err = elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   225  			if err != nil {
   226  				return fmt.Errorf("Failure configuring health check: %s", err)
   227  			}
   228  		}
   229  	}
   230  
   231  	return resourceAwsElbUpdate(d, meta)
   232  }
   233  
   234  func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
   235  	elbconn := meta.(*AWSClient).elbconn
   236  
   237  	// Retrieve the ELB properties for updating the state
   238  	describeElbOpts := &elb.DescribeLoadBalancer{
   239  		Names: []string{d.Id()},
   240  	}
   241  
   242  	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
   243  	if err != nil {
   244  		if ec2err, ok := err.(*elb.Error); ok && ec2err.Code == "LoadBalancerNotFound" {
   245  			// The ELB is gone now, so just remove it from the state
   246  			d.SetId("")
   247  			return nil
   248  		}
   249  
   250  		return fmt.Errorf("Error retrieving ELB: %s", err)
   251  	}
   252  	if len(describeResp.LoadBalancers) != 1 {
   253  		return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancers)
   254  	}
   255  
   256  	lb := describeResp.LoadBalancers[0]
   257  
   258  	d.Set("name", lb.LoadBalancerName)
   259  	d.Set("dns_name", lb.DNSName)
   260  	d.Set("internal", lb.Scheme == "internal")
   261  	d.Set("availability_zones", lb.AvailabilityZones)
   262  	d.Set("instances", flattenInstances(lb.Instances))
   263  	d.Set("listener", flattenListeners(lb.Listeners))
   264  	d.Set("security_groups", lb.SecurityGroups)
   265  	d.Set("subnets", lb.Subnets)
   266  
   267  	// There's only one health check, so save that to state as we
   268  	// currently can
   269  	if lb.HealthCheck.Target != "" {
   270  		d.Set("health_check", flattenHealthCheck(lb.HealthCheck))
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
   277  	elbconn := meta.(*AWSClient).elbconn
   278  
   279  	d.Partial(true)
   280  
   281  	// If we currently have instances, or did have instances,
   282  	// we want to figure out what to add and remove from the load
   283  	// balancer
   284  	if d.HasChange("instances") {
   285  		o, n := d.GetChange("instances")
   286  		os := o.(*schema.Set)
   287  		ns := n.(*schema.Set)
   288  		remove := expandStringList(os.Difference(ns).List())
   289  		add := expandStringList(ns.Difference(os).List())
   290  
   291  		if len(add) > 0 {
   292  			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancer{
   293  				LoadBalancerName: d.Id(),
   294  				Instances:        add,
   295  			}
   296  
   297  			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)
   298  			if err != nil {
   299  				return fmt.Errorf("Failure registering instances: %s", err)
   300  			}
   301  		}
   302  		if len(remove) > 0 {
   303  			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancer{
   304  				LoadBalancerName: d.Id(),
   305  				Instances:        remove,
   306  			}
   307  
   308  			_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
   309  			if err != nil {
   310  				return fmt.Errorf("Failure deregistering instances: %s", err)
   311  			}
   312  		}
   313  
   314  		d.SetPartial("instances")
   315  	}
   316  
   317  	log.Println("[INFO] outside modify attributes")
   318  	if d.HasChange("cross_zone_load_balancing") {
   319  		log.Println("[INFO] inside modify attributes")
   320  		attrs := elb.ModifyLoadBalancerAttributes{
   321  			LoadBalancerName: d.Get("name").(string),
   322  			LoadBalancerAttributes: elb.LoadBalancerAttributes{
   323  				CrossZoneLoadBalancingEnabled: d.Get("cross_zone_load_balancing").(bool),
   324  			},
   325  		}
   326  		_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
   327  		if err != nil {
   328  			return fmt.Errorf("Failure configuring cross zone balancing: %s", err)
   329  		}
   330  		d.SetPartial("cross_zone_load_balancing")
   331  	}
   332  
   333  	if d.HasChange("health_check") {
   334  		vs := d.Get("health_check").(*schema.Set).List()
   335  		if len(vs) > 0 {
   336  			check := vs[0].(map[string]interface{})
   337  			configureHealthCheckOpts := elb.ConfigureHealthCheck{
   338  				LoadBalancerName: d.Id(),
   339  				Check: elb.HealthCheck{
   340  					HealthyThreshold:   int64(check["healthy_threshold"].(int)),
   341  					UnhealthyThreshold: int64(check["unhealthy_threshold"].(int)),
   342  					Interval:           int64(check["interval"].(int)),
   343  					Target:             check["target"].(string),
   344  					Timeout:            int64(check["timeout"].(int)),
   345  				},
   346  			}
   347  			_, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   348  			if err != nil {
   349  				return fmt.Errorf("Failure configuring health check: %s", err)
   350  			}
   351  			d.SetPartial("health_check")
   352  		}
   353  	}
   354  
   355  	d.Partial(false)
   356  	return resourceAwsElbRead(d, meta)
   357  }
   358  
   359  func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
   360  	elbconn := meta.(*AWSClient).elbconn
   361  
   362  	log.Printf("[INFO] Deleting ELB: %s", d.Id())
   363  
   364  	// Destroy the load balancer
   365  	deleteElbOpts := elb.DeleteLoadBalancer{
   366  		LoadBalancerName: d.Id(),
   367  	}
   368  	if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
   369  		return fmt.Errorf("Error deleting ELB: %s", err)
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  func resourceAwsElbHealthCheckHash(v interface{}) int {
   376  	var buf bytes.Buffer
   377  	m := v.(map[string]interface{})
   378  	buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int)))
   379  	buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int)))
   380  	buf.WriteString(fmt.Sprintf("%s-", m["target"].(string)))
   381  	buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
   382  	buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int)))
   383  
   384  	return hashcode.String(buf.String())
   385  }
   386  
   387  func resourceAwsElbListenerHash(v interface{}) int {
   388  	var buf bytes.Buffer
   389  	m := v.(map[string]interface{})
   390  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   391  	buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string)))
   392  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   393  	buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string)))
   394  
   395  	if v, ok := m["ssl_certificate_id"]; ok {
   396  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   397  	}
   398  
   399  	return hashcode.String(buf.String())
   400  }