github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/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:      schema.HashString,
    47  			},
    48  
    49  			"instances": &schema.Schema{
    50  				Type:     schema.TypeSet,
    51  				Elem:     &schema.Schema{Type: schema.TypeString},
    52  				Optional: true,
    53  				Computed: true,
    54  				Set:      schema.HashString,
    55  			},
    56  
    57  			"security_groups": &schema.Schema{
    58  				Type:     schema.TypeSet,
    59  				Elem:     &schema.Schema{Type: schema.TypeString},
    60  				Optional: true,
    61  				Computed: true,
    62  				Set:      schema.HashString,
    63  			},
    64  
    65  			"source_security_group": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				Computed: true,
    69  			},
    70  
    71  			"subnets": &schema.Schema{
    72  				Type:     schema.TypeSet,
    73  				Elem:     &schema.Schema{Type: schema.TypeString},
    74  				Optional: true,
    75  				ForceNew: true,
    76  				Computed: true,
    77  				Set:      schema.HashString,
    78  			},
    79  
    80  			"idle_timeout": &schema.Schema{
    81  				Type:     schema.TypeInt,
    82  				Optional: true,
    83  				Default:  60,
    84  			},
    85  
    86  			"connection_draining": &schema.Schema{
    87  				Type:     schema.TypeBool,
    88  				Optional: true,
    89  				Default:  false,
    90  			},
    91  
    92  			"connection_draining_timeout": &schema.Schema{
    93  				Type:     schema.TypeInt,
    94  				Optional: true,
    95  				Default:  300,
    96  			},
    97  
    98  			"listener": &schema.Schema{
    99  				Type:     schema.TypeSet,
   100  				Required: true,
   101  				Elem: &schema.Resource{
   102  					Schema: map[string]*schema.Schema{
   103  						"instance_port": &schema.Schema{
   104  							Type:     schema.TypeInt,
   105  							Required: true,
   106  						},
   107  
   108  						"instance_protocol": &schema.Schema{
   109  							Type:     schema.TypeString,
   110  							Required: true,
   111  						},
   112  
   113  						"lb_port": &schema.Schema{
   114  							Type:     schema.TypeInt,
   115  							Required: true,
   116  						},
   117  
   118  						"lb_protocol": &schema.Schema{
   119  							Type:     schema.TypeString,
   120  							Required: true,
   121  						},
   122  
   123  						"ssl_certificate_id": &schema.Schema{
   124  							Type:     schema.TypeString,
   125  							Optional: true,
   126  						},
   127  					},
   128  				},
   129  				Set: resourceAwsElbListenerHash,
   130  			},
   131  
   132  			"health_check": &schema.Schema{
   133  				Type:     schema.TypeSet,
   134  				Optional: true,
   135  				Computed: true,
   136  				Elem: &schema.Resource{
   137  					Schema: map[string]*schema.Schema{
   138  						"healthy_threshold": &schema.Schema{
   139  							Type:     schema.TypeInt,
   140  							Required: true,
   141  						},
   142  
   143  						"unhealthy_threshold": &schema.Schema{
   144  							Type:     schema.TypeInt,
   145  							Required: true,
   146  						},
   147  
   148  						"target": &schema.Schema{
   149  							Type:     schema.TypeString,
   150  							Required: true,
   151  						},
   152  
   153  						"interval": &schema.Schema{
   154  							Type:     schema.TypeInt,
   155  							Required: true,
   156  						},
   157  
   158  						"timeout": &schema.Schema{
   159  							Type:     schema.TypeInt,
   160  							Required: true,
   161  						},
   162  					},
   163  				},
   164  				Set: resourceAwsElbHealthCheckHash,
   165  			},
   166  
   167  			"dns_name": &schema.Schema{
   168  				Type:     schema.TypeString,
   169  				Computed: true,
   170  			},
   171  
   172  			"zone_id": &schema.Schema{
   173  				Type:     schema.TypeString,
   174  				Computed: true,
   175  			},
   176  
   177  			"tags": tagsSchema(),
   178  		},
   179  	}
   180  }
   181  
   182  func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
   183  	elbconn := meta.(*AWSClient).elbconn
   184  
   185  	// Expand the "listener" set to aws-sdk-go compat []*elb.Listener
   186  	listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
   187  	if err != nil {
   188  		return err
   189  	}
   190  
   191  	tags := tagsFromMapELB(d.Get("tags").(map[string]interface{}))
   192  	// Provision the elb
   193  	elbOpts := &elb.CreateLoadBalancerInput{
   194  		LoadBalancerName: aws.String(d.Get("name").(string)),
   195  		Listeners:        listeners,
   196  		Tags:             tags,
   197  	}
   198  
   199  	if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) {
   200  		elbOpts.Scheme = aws.String("internal")
   201  	}
   202  
   203  	if v, ok := d.GetOk("availability_zones"); ok {
   204  		elbOpts.AvailabilityZones = expandStringList(v.(*schema.Set).List())
   205  	}
   206  
   207  	if v, ok := d.GetOk("security_groups"); ok {
   208  		elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List())
   209  	}
   210  
   211  	if v, ok := d.GetOk("subnets"); ok {
   212  		elbOpts.Subnets = expandStringList(v.(*schema.Set).List())
   213  	}
   214  
   215  	log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
   216  	if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil {
   217  		return fmt.Errorf("Error creating ELB: %s", err)
   218  	}
   219  
   220  	// Assign the elb's unique identifier for use later
   221  	d.SetId(d.Get("name").(string))
   222  	log.Printf("[INFO] ELB ID: %s", d.Id())
   223  
   224  	// Enable partial mode and record what we set
   225  	d.Partial(true)
   226  	d.SetPartial("name")
   227  	d.SetPartial("internal")
   228  	d.SetPartial("availability_zones")
   229  	d.SetPartial("listener")
   230  	d.SetPartial("security_groups")
   231  	d.SetPartial("subnets")
   232  
   233  	d.Set("tags", tagsToMapELB(tags))
   234  
   235  	return resourceAwsElbUpdate(d, meta)
   236  }
   237  
   238  func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
   239  	elbconn := meta.(*AWSClient).elbconn
   240  	elbName := d.Id()
   241  
   242  	// Retrieve the ELB properties for updating the state
   243  	describeElbOpts := &elb.DescribeLoadBalancersInput{
   244  		LoadBalancerNames: []*string{aws.String(elbName)},
   245  	}
   246  
   247  	describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
   248  	if err != nil {
   249  		if isLoadBalancerNotFound(err) {
   250  			// The ELB is gone now, so just remove it from the state
   251  			d.SetId("")
   252  			return nil
   253  		}
   254  
   255  		return fmt.Errorf("Error retrieving ELB: %s", err)
   256  	}
   257  	if len(describeResp.LoadBalancerDescriptions) != 1 {
   258  		return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions)
   259  	}
   260  
   261  	describeAttrsOpts := &elb.DescribeLoadBalancerAttributesInput{
   262  		LoadBalancerName: aws.String(elbName),
   263  	}
   264  	describeAttrsResp, err := elbconn.DescribeLoadBalancerAttributes(describeAttrsOpts)
   265  	if err != nil {
   266  		if isLoadBalancerNotFound(err) {
   267  			// The ELB is gone now, so just remove it from the state
   268  			d.SetId("")
   269  			return nil
   270  		}
   271  
   272  		return fmt.Errorf("Error retrieving ELB: %s", err)
   273  	}
   274  
   275  	lbAttrs := describeAttrsResp.LoadBalancerAttributes
   276  
   277  	lb := describeResp.LoadBalancerDescriptions[0]
   278  
   279  	d.Set("name", *lb.LoadBalancerName)
   280  	d.Set("dns_name", *lb.DNSName)
   281  	d.Set("zone_id", *lb.CanonicalHostedZoneNameID)
   282  	d.Set("internal", *lb.Scheme == "internal")
   283  	d.Set("availability_zones", lb.AvailabilityZones)
   284  	d.Set("instances", flattenInstances(lb.Instances))
   285  	d.Set("listener", flattenListeners(lb.ListenerDescriptions))
   286  	d.Set("security_groups", lb.SecurityGroups)
   287  	if lb.SourceSecurityGroup != nil {
   288  		d.Set("source_security_group", lb.SourceSecurityGroup.GroupName)
   289  	}
   290  	d.Set("subnets", lb.Subnets)
   291  	d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
   292  	d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
   293  	d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout)
   294  
   295  	resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
   296  		LoadBalancerNames: []*string{lb.LoadBalancerName},
   297  	})
   298  
   299  	var et []*elb.Tag
   300  	if len(resp.TagDescriptions) > 0 {
   301  		et = resp.TagDescriptions[0].Tags
   302  	}
   303  	d.Set("tags", tagsToMapELB(et))
   304  	// There's only one health check, so save that to state as we
   305  	// currently can
   306  	if *lb.HealthCheck.Target != "" {
   307  		d.Set("health_check", flattenHealthCheck(lb.HealthCheck))
   308  	}
   309  
   310  	return nil
   311  }
   312  
   313  func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
   314  	elbconn := meta.(*AWSClient).elbconn
   315  
   316  	d.Partial(true)
   317  
   318  	if d.HasChange("listener") {
   319  		o, n := d.GetChange("listener")
   320  		os := o.(*schema.Set)
   321  		ns := n.(*schema.Set)
   322  
   323  		remove, _ := expandListeners(os.Difference(ns).List())
   324  		add, _ := expandListeners(ns.Difference(os).List())
   325  
   326  		if len(remove) > 0 {
   327  			ports := make([]*int64, 0, len(remove))
   328  			for _, listener := range remove {
   329  				ports = append(ports, listener.LoadBalancerPort)
   330  			}
   331  
   332  			deleteListenersOpts := &elb.DeleteLoadBalancerListenersInput{
   333  				LoadBalancerName:  aws.String(d.Id()),
   334  				LoadBalancerPorts: ports,
   335  			}
   336  
   337  			_, err := elbconn.DeleteLoadBalancerListeners(deleteListenersOpts)
   338  			if err != nil {
   339  				return fmt.Errorf("Failure removing outdated listeners: %s", err)
   340  			}
   341  		}
   342  
   343  		if len(add) > 0 {
   344  			createListenersOpts := &elb.CreateLoadBalancerListenersInput{
   345  				LoadBalancerName: aws.String(d.Id()),
   346  				Listeners:        add,
   347  			}
   348  
   349  			_, err := elbconn.CreateLoadBalancerListeners(createListenersOpts)
   350  			if err != nil {
   351  				return fmt.Errorf("Failure adding new or updated listeners: %s", err)
   352  			}
   353  		}
   354  
   355  		d.SetPartial("listener")
   356  	}
   357  
   358  	// If we currently have instances, or did have instances,
   359  	// we want to figure out what to add and remove from the load
   360  	// balancer
   361  	if d.HasChange("instances") {
   362  		o, n := d.GetChange("instances")
   363  		os := o.(*schema.Set)
   364  		ns := n.(*schema.Set)
   365  		remove := expandInstanceString(os.Difference(ns).List())
   366  		add := expandInstanceString(ns.Difference(os).List())
   367  
   368  		if len(add) > 0 {
   369  			registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{
   370  				LoadBalancerName: aws.String(d.Id()),
   371  				Instances:        add,
   372  			}
   373  
   374  			_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)
   375  			if err != nil {
   376  				return fmt.Errorf("Failure registering instances: %s", err)
   377  			}
   378  		}
   379  		if len(remove) > 0 {
   380  			deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
   381  				LoadBalancerName: aws.String(d.Id()),
   382  				Instances:        remove,
   383  			}
   384  
   385  			_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
   386  			if err != nil {
   387  				return fmt.Errorf("Failure deregistering instances: %s", err)
   388  			}
   389  		}
   390  
   391  		d.SetPartial("instances")
   392  	}
   393  
   394  	log.Println("[INFO] outside modify attributes")
   395  	if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("connection_draining") || d.HasChange("connection_draining_timeout") {
   396  		log.Println("[INFO] inside modify attributes")
   397  		attrs := elb.ModifyLoadBalancerAttributesInput{
   398  			LoadBalancerName: aws.String(d.Get("name").(string)),
   399  			LoadBalancerAttributes: &elb.LoadBalancerAttributes{
   400  				CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{
   401  					Enabled: aws.Boolean(d.Get("cross_zone_load_balancing").(bool)),
   402  				},
   403  				ConnectionSettings: &elb.ConnectionSettings{
   404  					IdleTimeout: aws.Long(int64(d.Get("idle_timeout").(int))),
   405  				},
   406  				ConnectionDraining: &elb.ConnectionDraining{
   407  					Enabled: aws.Boolean(d.Get("connection_draining").(bool)),
   408  					Timeout: aws.Long(int64(d.Get("connection_draining_timeout").(int))),
   409  				},
   410  			},
   411  		}
   412  		_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
   413  		if err != nil {
   414  			return fmt.Errorf("Failure configuring elb attributes: %s", err)
   415  		}
   416  		d.SetPartial("cross_zone_load_balancing")
   417  		d.SetPartial("idle_timeout")
   418  		d.SetPartial("connection_draining")
   419  		d.SetPartial("connection_draining_timeout")
   420  	}
   421  
   422  	if d.HasChange("health_check") {
   423  		vs := d.Get("health_check").(*schema.Set).List()
   424  		if len(vs) > 0 {
   425  			check := vs[0].(map[string]interface{})
   426  			configureHealthCheckOpts := elb.ConfigureHealthCheckInput{
   427  				LoadBalancerName: aws.String(d.Id()),
   428  				HealthCheck: &elb.HealthCheck{
   429  					HealthyThreshold:   aws.Long(int64(check["healthy_threshold"].(int))),
   430  					UnhealthyThreshold: aws.Long(int64(check["unhealthy_threshold"].(int))),
   431  					Interval:           aws.Long(int64(check["interval"].(int))),
   432  					Target:             aws.String(check["target"].(string)),
   433  					Timeout:            aws.Long(int64(check["timeout"].(int))),
   434  				},
   435  			}
   436  			_, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
   437  			if err != nil {
   438  				return fmt.Errorf("Failure configuring health check: %s", err)
   439  			}
   440  			d.SetPartial("health_check")
   441  		}
   442  	}
   443  
   444  	if d.HasChange("security_groups") {
   445  		groups := d.Get("security_groups").(*schema.Set).List()
   446  
   447  		applySecurityGroupsOpts := elb.ApplySecurityGroupsToLoadBalancerInput{
   448  			LoadBalancerName: aws.String(d.Id()),
   449  			SecurityGroups:   expandStringList(groups),
   450  		}
   451  
   452  		_, err := elbconn.ApplySecurityGroupsToLoadBalancer(&applySecurityGroupsOpts)
   453  		if err != nil {
   454  			return fmt.Errorf("Failure applying security groups: %s", err)
   455  		}
   456  
   457  		d.SetPartial("security_groups")
   458  	}
   459  
   460  	if err := setTagsELB(elbconn, d); err != nil {
   461  		return err
   462  	}
   463  
   464  	d.SetPartial("tags")
   465  	d.Partial(false)
   466  
   467  	return resourceAwsElbRead(d, meta)
   468  }
   469  
   470  func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
   471  	elbconn := meta.(*AWSClient).elbconn
   472  
   473  	log.Printf("[INFO] Deleting ELB: %s", d.Id())
   474  
   475  	// Destroy the load balancer
   476  	deleteElbOpts := elb.DeleteLoadBalancerInput{
   477  		LoadBalancerName: aws.String(d.Id()),
   478  	}
   479  	if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
   480  		return fmt.Errorf("Error deleting ELB: %s", err)
   481  	}
   482  
   483  	return nil
   484  }
   485  
   486  func resourceAwsElbHealthCheckHash(v interface{}) int {
   487  	var buf bytes.Buffer
   488  	m := v.(map[string]interface{})
   489  	buf.WriteString(fmt.Sprintf("%d-", m["healthy_threshold"].(int)))
   490  	buf.WriteString(fmt.Sprintf("%d-", m["unhealthy_threshold"].(int)))
   491  	buf.WriteString(fmt.Sprintf("%s-", m["target"].(string)))
   492  	buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
   493  	buf.WriteString(fmt.Sprintf("%d-", m["timeout"].(int)))
   494  
   495  	return hashcode.String(buf.String())
   496  }
   497  
   498  func resourceAwsElbListenerHash(v interface{}) int {
   499  	var buf bytes.Buffer
   500  	m := v.(map[string]interface{})
   501  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   502  	buf.WriteString(fmt.Sprintf("%s-", m["instance_protocol"].(string)))
   503  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   504  	buf.WriteString(fmt.Sprintf("%s-", m["lb_protocol"].(string)))
   505  
   506  	if v, ok := m["ssl_certificate_id"]; ok {
   507  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   508  	}
   509  
   510  	return hashcode.String(buf.String())
   511  }
   512  
   513  func isLoadBalancerNotFound(err error) bool {
   514  	elberr, ok := err.(aws.APIError)
   515  	return ok && elberr.Code == "LoadBalancerNotFound"
   516  }