mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-14 01:13:59 -06:00
Changes made manually to the ELB property were not being set and Terraform was reporting now changes to infra was to be made on refresh
797 lines
22 KiB
Go
797 lines
22 KiB
Go
package aws
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
|
"github.com/aws/aws-sdk-go/service/elb"
|
|
"github.com/hashicorp/terraform/helper/hashcode"
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
func resourceAwsElb() *schema.Resource {
|
|
return &schema.Resource{
|
|
Create: resourceAwsElbCreate,
|
|
Read: resourceAwsElbRead,
|
|
Update: resourceAwsElbUpdate,
|
|
Delete: resourceAwsElbDelete,
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
"name": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
ForceNew: true,
|
|
ValidateFunc: validateElbName,
|
|
},
|
|
|
|
"internal": &schema.Schema{
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
ForceNew: true,
|
|
Computed: true,
|
|
},
|
|
|
|
"cross_zone_load_balancing": &schema.Schema{
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
},
|
|
|
|
"availability_zones": &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
Optional: true,
|
|
Computed: true,
|
|
Set: schema.HashString,
|
|
},
|
|
|
|
"instances": &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
Optional: true,
|
|
Computed: true,
|
|
Set: schema.HashString,
|
|
},
|
|
|
|
"security_groups": &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
Optional: true,
|
|
Computed: true,
|
|
Set: schema.HashString,
|
|
},
|
|
|
|
"source_security_group": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
Computed: true,
|
|
},
|
|
|
|
"source_security_group_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
|
|
"subnets": &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
Optional: true,
|
|
Computed: true,
|
|
Set: schema.HashString,
|
|
},
|
|
|
|
"idle_timeout": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 60,
|
|
},
|
|
|
|
"connection_draining": &schema.Schema{
|
|
Type: schema.TypeBool,
|
|
Optional: true,
|
|
Default: false,
|
|
},
|
|
|
|
"connection_draining_timeout": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 300,
|
|
},
|
|
|
|
"access_logs": &schema.Schema{
|
|
Type: schema.TypeList,
|
|
Optional: true,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"interval": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Optional: true,
|
|
Default: 60,
|
|
},
|
|
"bucket": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
"bucket_prefix": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
"listener": &schema.Schema{
|
|
Type: schema.TypeSet,
|
|
Required: true,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"instance_port": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"instance_protocol": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"lb_port": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"lb_protocol": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"ssl_certificate_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Optional: true,
|
|
},
|
|
},
|
|
},
|
|
Set: resourceAwsElbListenerHash,
|
|
},
|
|
|
|
"health_check": &schema.Schema{
|
|
Type: schema.TypeList,
|
|
Optional: true,
|
|
Computed: true,
|
|
Elem: &schema.Resource{
|
|
Schema: map[string]*schema.Schema{
|
|
"healthy_threshold": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"unhealthy_threshold": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"target": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Required: true,
|
|
},
|
|
|
|
"interval": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
|
|
"timeout": &schema.Schema{
|
|
Type: schema.TypeInt,
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
"dns_name": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
|
|
"zone_id": &schema.Schema{
|
|
Type: schema.TypeString,
|
|
Computed: true,
|
|
},
|
|
|
|
"tags": tagsSchema(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
|
|
elbconn := meta.(*AWSClient).elbconn
|
|
|
|
// Expand the "listener" set to aws-sdk-go compat []*elb.Listener
|
|
listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var elbName string
|
|
if v, ok := d.GetOk("name"); ok {
|
|
elbName = v.(string)
|
|
} else {
|
|
elbName = resource.PrefixedUniqueId("tf-lb-")
|
|
d.Set("name", elbName)
|
|
}
|
|
|
|
tags := tagsFromMapELB(d.Get("tags").(map[string]interface{}))
|
|
// Provision the elb
|
|
elbOpts := &elb.CreateLoadBalancerInput{
|
|
LoadBalancerName: aws.String(elbName),
|
|
Listeners: listeners,
|
|
Tags: tags,
|
|
}
|
|
|
|
if scheme, ok := d.GetOk("internal"); ok && scheme.(bool) {
|
|
elbOpts.Scheme = aws.String("internal")
|
|
}
|
|
|
|
if v, ok := d.GetOk("availability_zones"); ok {
|
|
elbOpts.AvailabilityZones = expandStringList(v.(*schema.Set).List())
|
|
}
|
|
|
|
if v, ok := d.GetOk("security_groups"); ok {
|
|
elbOpts.SecurityGroups = expandStringList(v.(*schema.Set).List())
|
|
}
|
|
|
|
if v, ok := d.GetOk("subnets"); ok {
|
|
elbOpts.Subnets = expandStringList(v.(*schema.Set).List())
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
|
|
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
|
|
_, err := elbconn.CreateLoadBalancer(elbOpts)
|
|
|
|
if err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
// Check for IAM SSL Cert error, eventual consistancy issue
|
|
if awsErr.Code() == "CertificateNotFound" {
|
|
return resource.RetryableError(
|
|
fmt.Errorf("[WARN] Error creating ELB Listener with SSL Cert, retrying: %s", err))
|
|
}
|
|
}
|
|
return resource.NonRetryableError(err)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Assign the elb's unique identifier for use later
|
|
d.SetId(elbName)
|
|
log.Printf("[INFO] ELB ID: %s", d.Id())
|
|
|
|
// Enable partial mode and record what we set
|
|
d.Partial(true)
|
|
d.SetPartial("name")
|
|
d.SetPartial("internal")
|
|
d.SetPartial("availability_zones")
|
|
d.SetPartial("listener")
|
|
d.SetPartial("security_groups")
|
|
d.SetPartial("subnets")
|
|
|
|
d.Set("tags", tagsToMapELB(tags))
|
|
|
|
return resourceAwsElbUpdate(d, meta)
|
|
}
|
|
|
|
func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
|
|
elbconn := meta.(*AWSClient).elbconn
|
|
elbName := d.Id()
|
|
|
|
// Retrieve the ELB properties for updating the state
|
|
describeElbOpts := &elb.DescribeLoadBalancersInput{
|
|
LoadBalancerNames: []*string{aws.String(elbName)},
|
|
}
|
|
|
|
describeResp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
|
|
if err != nil {
|
|
if isLoadBalancerNotFound(err) {
|
|
// The ELB is gone now, so just remove it from the state
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("Error retrieving ELB: %s", err)
|
|
}
|
|
if len(describeResp.LoadBalancerDescriptions) != 1 {
|
|
return fmt.Errorf("Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions)
|
|
}
|
|
|
|
describeAttrsOpts := &elb.DescribeLoadBalancerAttributesInput{
|
|
LoadBalancerName: aws.String(elbName),
|
|
}
|
|
describeAttrsResp, err := elbconn.DescribeLoadBalancerAttributes(describeAttrsOpts)
|
|
if err != nil {
|
|
if isLoadBalancerNotFound(err) {
|
|
// The ELB is gone now, so just remove it from the state
|
|
d.SetId("")
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("Error retrieving ELB: %s", err)
|
|
}
|
|
|
|
lbAttrs := describeAttrsResp.LoadBalancerAttributes
|
|
|
|
lb := describeResp.LoadBalancerDescriptions[0]
|
|
|
|
d.Set("name", *lb.LoadBalancerName)
|
|
d.Set("dns_name", *lb.DNSName)
|
|
d.Set("zone_id", *lb.CanonicalHostedZoneNameID)
|
|
d.Set("internal", *lb.Scheme == "internal")
|
|
d.Set("availability_zones", flattenStringList(lb.AvailabilityZones))
|
|
d.Set("instances", flattenInstances(lb.Instances))
|
|
d.Set("listener", flattenListeners(lb.ListenerDescriptions))
|
|
d.Set("security_groups", flattenStringList(lb.SecurityGroups))
|
|
if lb.SourceSecurityGroup != nil {
|
|
group := lb.SourceSecurityGroup.GroupName
|
|
if lb.SourceSecurityGroup.OwnerAlias != nil && *lb.SourceSecurityGroup.OwnerAlias != "" {
|
|
group = aws.String(*lb.SourceSecurityGroup.OwnerAlias + "/" + *lb.SourceSecurityGroup.GroupName)
|
|
}
|
|
d.Set("source_security_group", group)
|
|
|
|
// Manually look up the ELB Security Group ID, since it's not provided
|
|
var elbVpc string
|
|
if lb.VPCId != nil {
|
|
elbVpc = *lb.VPCId
|
|
sgId, err := sourceSGIdByName(meta, *lb.SourceSecurityGroup.GroupName, elbVpc)
|
|
if err != nil {
|
|
return fmt.Errorf("[WARN] Error looking up ELB Security Group ID: %s", err)
|
|
} else {
|
|
d.Set("source_security_group_id", sgId)
|
|
}
|
|
}
|
|
}
|
|
d.Set("subnets", flattenStringList(lb.Subnets))
|
|
d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
|
|
d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
|
|
d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout)
|
|
d.Set("cross_zone_load_balancing", lbAttrs.CrossZoneLoadBalancing.Enabled)
|
|
if lbAttrs.AccessLog != nil {
|
|
if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
|
|
LoadBalancerNames: []*string{lb.LoadBalancerName},
|
|
})
|
|
|
|
var et []*elb.Tag
|
|
if len(resp.TagDescriptions) > 0 {
|
|
et = resp.TagDescriptions[0].Tags
|
|
}
|
|
d.Set("tags", tagsToMapELB(et))
|
|
|
|
// There's only one health check, so save that to state as we
|
|
// currently can
|
|
if *lb.HealthCheck.Target != "" {
|
|
d.Set("health_check", flattenHealthCheck(lb.HealthCheck))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
elbconn := meta.(*AWSClient).elbconn
|
|
|
|
d.Partial(true)
|
|
|
|
if d.HasChange("listener") {
|
|
o, n := d.GetChange("listener")
|
|
os := o.(*schema.Set)
|
|
ns := n.(*schema.Set)
|
|
|
|
remove, _ := expandListeners(os.Difference(ns).List())
|
|
add, _ := expandListeners(ns.Difference(os).List())
|
|
|
|
if len(remove) > 0 {
|
|
ports := make([]*int64, 0, len(remove))
|
|
for _, listener := range remove {
|
|
ports = append(ports, listener.LoadBalancerPort)
|
|
}
|
|
|
|
deleteListenersOpts := &elb.DeleteLoadBalancerListenersInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
LoadBalancerPorts: ports,
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB Delete Listeners opts: %s", deleteListenersOpts)
|
|
_, err := elbconn.DeleteLoadBalancerListeners(deleteListenersOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure removing outdated ELB listeners: %s", err)
|
|
}
|
|
}
|
|
|
|
if len(add) > 0 {
|
|
createListenersOpts := &elb.CreateLoadBalancerListenersInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
Listeners: add,
|
|
}
|
|
|
|
// Occasionally AWS will error with a 'duplicate listener', without any
|
|
// other listeners on the ELB. Retry here to eliminate that.
|
|
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
|
|
log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts)
|
|
if _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts); err != nil {
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
if awsErr.Code() == "DuplicateListener" {
|
|
log.Printf("[DEBUG] Duplicate listener found for ELB (%s), retrying", d.Id())
|
|
return resource.RetryableError(awsErr)
|
|
}
|
|
if awsErr.Code() == "CertificateNotFound" && strings.Contains(awsErr.Message(), "Server Certificate not found for the key: arn") {
|
|
log.Printf("[DEBUG] SSL Cert not found for given ARN, retrying")
|
|
return resource.RetryableError(awsErr)
|
|
}
|
|
}
|
|
|
|
// Didn't recognize the error, so shouldn't retry.
|
|
return resource.NonRetryableError(err)
|
|
}
|
|
// Successful creation
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("Failure adding new or updated ELB listeners: %s", err)
|
|
}
|
|
}
|
|
|
|
d.SetPartial("listener")
|
|
}
|
|
|
|
// If we currently have instances, or did have instances,
|
|
// we want to figure out what to add and remove from the load
|
|
// balancer
|
|
if d.HasChange("instances") {
|
|
o, n := d.GetChange("instances")
|
|
os := o.(*schema.Set)
|
|
ns := n.(*schema.Set)
|
|
remove := expandInstanceString(os.Difference(ns).List())
|
|
add := expandInstanceString(ns.Difference(os).List())
|
|
|
|
if len(add) > 0 {
|
|
registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
Instances: add,
|
|
}
|
|
|
|
_, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure registering instances with ELB: %s", err)
|
|
}
|
|
}
|
|
if len(remove) > 0 {
|
|
deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
Instances: remove,
|
|
}
|
|
|
|
_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure deregistering instances from ELB: %s", err)
|
|
}
|
|
}
|
|
|
|
d.SetPartial("instances")
|
|
}
|
|
|
|
if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") {
|
|
attrs := elb.ModifyLoadBalancerAttributesInput{
|
|
LoadBalancerName: aws.String(d.Get("name").(string)),
|
|
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
|
|
CrossZoneLoadBalancing: &elb.CrossZoneLoadBalancing{
|
|
Enabled: aws.Bool(d.Get("cross_zone_load_balancing").(bool)),
|
|
},
|
|
ConnectionSettings: &elb.ConnectionSettings{
|
|
IdleTimeout: aws.Int64(int64(d.Get("idle_timeout").(int))),
|
|
},
|
|
},
|
|
}
|
|
|
|
logs := d.Get("access_logs").([]interface{})
|
|
if len(logs) > 1 {
|
|
return fmt.Errorf("Only one access logs config per ELB is supported")
|
|
} else if len(logs) == 1 {
|
|
log := logs[0].(map[string]interface{})
|
|
accessLog := &elb.AccessLog{
|
|
Enabled: aws.Bool(true),
|
|
EmitInterval: aws.Int64(int64(log["interval"].(int))),
|
|
S3BucketName: aws.String(log["bucket"].(string)),
|
|
}
|
|
|
|
if log["bucket_prefix"] != "" {
|
|
accessLog.S3BucketPrefix = aws.String(log["bucket_prefix"].(string))
|
|
}
|
|
|
|
attrs.LoadBalancerAttributes.AccessLog = accessLog
|
|
} else if len(logs) == 0 {
|
|
// disable access logs
|
|
attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{
|
|
Enabled: aws.Bool(false),
|
|
}
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs)
|
|
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
|
|
}
|
|
|
|
d.SetPartial("cross_zone_load_balancing")
|
|
d.SetPartial("idle_timeout")
|
|
d.SetPartial("connection_draining_timeout")
|
|
}
|
|
|
|
// We have to do these changes separately from everything else since
|
|
// they have some weird undocumented rules. You can't set the timeout
|
|
// without having connection draining to true, so we set that to true,
|
|
// set the timeout, then reset it to false if requested.
|
|
if d.HasChange("connection_draining") || d.HasChange("connection_draining_timeout") {
|
|
// We do timeout changes first since they require us to set draining
|
|
// to true for a hot second.
|
|
if d.HasChange("connection_draining_timeout") {
|
|
attrs := elb.ModifyLoadBalancerAttributesInput{
|
|
LoadBalancerName: aws.String(d.Get("name").(string)),
|
|
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
|
|
ConnectionDraining: &elb.ConnectionDraining{
|
|
Enabled: aws.Bool(true),
|
|
Timeout: aws.Int64(int64(d.Get("connection_draining_timeout").(int))),
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
|
|
}
|
|
|
|
d.SetPartial("connection_draining_timeout")
|
|
}
|
|
|
|
// Then we always set connection draining even if there is no change.
|
|
// This lets us reset to "false" if requested even with a timeout
|
|
// change.
|
|
attrs := elb.ModifyLoadBalancerAttributesInput{
|
|
LoadBalancerName: aws.String(d.Get("name").(string)),
|
|
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
|
|
ConnectionDraining: &elb.ConnectionDraining{
|
|
Enabled: aws.Bool(d.Get("connection_draining").(bool)),
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
|
|
}
|
|
|
|
d.SetPartial("connection_draining")
|
|
}
|
|
|
|
if d.HasChange("health_check") {
|
|
hc := d.Get("health_check").([]interface{})
|
|
if len(hc) > 1 {
|
|
return fmt.Errorf("Only one health check per ELB is supported")
|
|
} else if len(hc) > 0 {
|
|
check := hc[0].(map[string]interface{})
|
|
configureHealthCheckOpts := elb.ConfigureHealthCheckInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
HealthCheck: &elb.HealthCheck{
|
|
HealthyThreshold: aws.Int64(int64(check["healthy_threshold"].(int))),
|
|
UnhealthyThreshold: aws.Int64(int64(check["unhealthy_threshold"].(int))),
|
|
Interval: aws.Int64(int64(check["interval"].(int))),
|
|
Target: aws.String(check["target"].(string)),
|
|
Timeout: aws.Int64(int64(check["timeout"].(int))),
|
|
},
|
|
}
|
|
_, err := elbconn.ConfigureHealthCheck(&configureHealthCheckOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure configuring health check for ELB: %s", err)
|
|
}
|
|
d.SetPartial("health_check")
|
|
}
|
|
}
|
|
|
|
if d.HasChange("security_groups") {
|
|
groups := d.Get("security_groups").(*schema.Set).List()
|
|
|
|
applySecurityGroupsOpts := elb.ApplySecurityGroupsToLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
SecurityGroups: expandStringList(groups),
|
|
}
|
|
|
|
_, err := elbconn.ApplySecurityGroupsToLoadBalancer(&applySecurityGroupsOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure applying security groups to ELB: %s", err)
|
|
}
|
|
|
|
d.SetPartial("security_groups")
|
|
}
|
|
|
|
if d.HasChange("availability_zones") {
|
|
o, n := d.GetChange("availability_zones")
|
|
os := o.(*schema.Set)
|
|
ns := n.(*schema.Set)
|
|
|
|
removed := expandStringList(os.Difference(ns).List())
|
|
added := expandStringList(ns.Difference(os).List())
|
|
|
|
if len(added) > 0 {
|
|
enableOpts := &elb.EnableAvailabilityZonesForLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
AvailabilityZones: added,
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB enable availability zones opts: %s", enableOpts)
|
|
_, err := elbconn.EnableAvailabilityZonesForLoadBalancer(enableOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure enabling ELB availability zones: %s", err)
|
|
}
|
|
}
|
|
|
|
if len(removed) > 0 {
|
|
disableOpts := &elb.DisableAvailabilityZonesForLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
AvailabilityZones: removed,
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB disable availability zones opts: %s", disableOpts)
|
|
_, err := elbconn.DisableAvailabilityZonesForLoadBalancer(disableOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure disabling ELB availability zones: %s", err)
|
|
}
|
|
}
|
|
|
|
d.SetPartial("availability_zones")
|
|
}
|
|
|
|
if d.HasChange("subnets") {
|
|
o, n := d.GetChange("subnets")
|
|
os := o.(*schema.Set)
|
|
ns := n.(*schema.Set)
|
|
|
|
removed := expandStringList(os.Difference(ns).List())
|
|
added := expandStringList(ns.Difference(os).List())
|
|
|
|
if len(added) > 0 {
|
|
attachOpts := &elb.AttachLoadBalancerToSubnetsInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
Subnets: added,
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB attach subnets opts: %s", attachOpts)
|
|
_, err := elbconn.AttachLoadBalancerToSubnets(attachOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure adding ELB subnets: %s", err)
|
|
}
|
|
}
|
|
|
|
if len(removed) > 0 {
|
|
detachOpts := &elb.DetachLoadBalancerFromSubnetsInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
Subnets: removed,
|
|
}
|
|
|
|
log.Printf("[DEBUG] ELB detach subnets opts: %s", detachOpts)
|
|
_, err := elbconn.DetachLoadBalancerFromSubnets(detachOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("Failure removing ELB subnets: %s", err)
|
|
}
|
|
}
|
|
|
|
d.SetPartial("subnets")
|
|
}
|
|
|
|
if err := setTagsELB(elbconn, d); err != nil {
|
|
return err
|
|
}
|
|
|
|
d.SetPartial("tags")
|
|
d.Partial(false)
|
|
|
|
return resourceAwsElbRead(d, meta)
|
|
}
|
|
|
|
func resourceAwsElbDelete(d *schema.ResourceData, meta interface{}) error {
|
|
elbconn := meta.(*AWSClient).elbconn
|
|
|
|
log.Printf("[INFO] Deleting ELB: %s", d.Id())
|
|
|
|
// Destroy the load balancer
|
|
deleteElbOpts := elb.DeleteLoadBalancerInput{
|
|
LoadBalancerName: aws.String(d.Id()),
|
|
}
|
|
if _, err := elbconn.DeleteLoadBalancer(&deleteElbOpts); err != nil {
|
|
return fmt.Errorf("Error deleting ELB: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func resourceAwsElbListenerHash(v interface{}) int {
|
|
var buf bytes.Buffer
|
|
m := v.(map[string]interface{})
|
|
buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
|
|
buf.WriteString(fmt.Sprintf("%s-",
|
|
strings.ToLower(m["instance_protocol"].(string))))
|
|
buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
|
|
buf.WriteString(fmt.Sprintf("%s-",
|
|
strings.ToLower(m["lb_protocol"].(string))))
|
|
|
|
if v, ok := m["ssl_certificate_id"]; ok {
|
|
buf.WriteString(fmt.Sprintf("%s-", v.(string)))
|
|
}
|
|
|
|
return hashcode.String(buf.String())
|
|
}
|
|
|
|
func isLoadBalancerNotFound(err error) bool {
|
|
elberr, ok := err.(awserr.Error)
|
|
return ok && elberr.Code() == "LoadBalancerNotFound"
|
|
}
|
|
|
|
func sourceSGIdByName(meta interface{}, sg, vpcId string) (string, error) {
|
|
conn := meta.(*AWSClient).ec2conn
|
|
var filters []*ec2.Filter
|
|
var sgFilterName, sgFilterVPCID *ec2.Filter
|
|
sgFilterName = &ec2.Filter{
|
|
Name: aws.String("group-name"),
|
|
Values: []*string{aws.String(sg)},
|
|
}
|
|
|
|
if vpcId != "" {
|
|
sgFilterVPCID = &ec2.Filter{
|
|
Name: aws.String("vpc-id"),
|
|
Values: []*string{aws.String(vpcId)},
|
|
}
|
|
}
|
|
|
|
filters = append(filters, sgFilterName)
|
|
|
|
if sgFilterVPCID != nil {
|
|
filters = append(filters, sgFilterVPCID)
|
|
}
|
|
|
|
req := &ec2.DescribeSecurityGroupsInput{
|
|
Filters: filters,
|
|
}
|
|
resp, err := conn.DescribeSecurityGroups(req)
|
|
if err != nil {
|
|
if ec2err, ok := err.(awserr.Error); ok {
|
|
if ec2err.Code() == "InvalidSecurityGroupID.NotFound" ||
|
|
ec2err.Code() == "InvalidGroup.NotFound" {
|
|
resp = nil
|
|
err = nil
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("Error on ELB SG look up: %s", err)
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
if resp == nil || len(resp.SecurityGroups) == 0 {
|
|
return "", fmt.Errorf("No security groups found for name %s and vpc id %s", sg, vpcId)
|
|
}
|
|
|
|
group := resp.SecurityGroups[0]
|
|
return *group.GroupId, nil
|
|
}
|