Skip to content

Commit

Permalink
Merge pull request #1956 from terraform-providers/b-nlb-eip
Browse files Browse the repository at this point in the history
r/aws_lb: Allow assigning EIP to network LB
  • Loading branch information
radeksimko authored Oct 20, 2017
2 parents a61d2ed + f9fbfa2 commit 3056b04
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 12 deletions.
78 changes: 66 additions & 12 deletions aws/resource_aws_lb.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,7 @@ func resourceAwsLbCreate(d *schema.ResourceData, meta interface{}) error {
}

if subnetMap["allocation_id"].(string) != "" {
elbOpts.SubnetMappings[i] = &elbv2.SubnetMapping{
AllocationId: aws.String(subnetMap["allocation_id"].(string)),
}
elbOpts.SubnetMappings[i].AllocationId = aws.String(subnetMap["allocation_id"].(string))
}
}
}
Expand Down Expand Up @@ -462,11 +460,18 @@ func resourceAwsLbDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error deleting ALB: %s", err)
}

err := cleanupLBNetworkInterfaces(meta.(*AWSClient).ec2conn, d.Id())
conn := meta.(*AWSClient).ec2conn

err := cleanupLBNetworkInterfaces(conn, d.Id())
if err != nil {
log.Printf("[WARN] Failed to cleanup ENIs for ALB %q: %#v", d.Id(), err)
}

err = waitForNLBNetworkInterfacesToDetach(conn, d.Id())
if err != nil {
log.Printf("[WARN] Failed to wait for ENIs to disappear for NLB %q: %#v", d.Id(), err)
}

return nil
}

Expand All @@ -475,15 +480,11 @@ func resourceAwsLbDelete(d *schema.ResourceData, meta interface{}) error {
// which then blocks IGW, SG or VPC on deletion
// So we make the cleanup "synchronous" here
func cleanupLBNetworkInterfaces(conn *ec2.EC2, lbArn string) error {
re := regexp.MustCompile("([^/]+/[^/]+/[^/]+)$")
matches := re.FindStringSubmatch(lbArn)
if len(matches) != 2 {
return fmt.Errorf("Unexpected ARN format: %q", lbArn)
name, err := getLbNameFromArn(lbArn)
if err != nil {
return err
}

// e.g. app/example-alb/b26e625cdde161e6
name := matches[1]

out, err := conn.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{
Expand All @@ -500,7 +501,7 @@ func cleanupLBNetworkInterfaces(conn *ec2.EC2, lbArn string) error {
return err
}

log.Printf("[DEBUG] Found %d ENIs to cleanup for ALB %q",
log.Printf("[DEBUG] Found %d ENIs to cleanup for LB %q",
len(out.NetworkInterfaces), name)

if len(out.NetworkInterfaces) == 0 {
Expand All @@ -521,6 +522,59 @@ func cleanupLBNetworkInterfaces(conn *ec2.EC2, lbArn string) error {
return nil
}

func waitForNLBNetworkInterfacesToDetach(conn *ec2.EC2, lbArn string) error {
name, err := getLbNameFromArn(lbArn)
if err != nil {
return err
}

// We cannot cleanup these ENIs ourselves as that would result in
// OperationNotPermitted: You are not allowed to manage 'ela-attach' attachments.
// yet presence of these ENIs may prevent us from deleting EIPs associated w/ the NLB

return resource.Retry(1*time.Minute, func() *resource.RetryError {
out, err := conn.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("attachment.instance-owner-id"),
Values: []*string{aws.String("amazon-aws")},
},
{
Name: aws.String("attachment.attachment-id"),
Values: []*string{aws.String("ela-attach-*")},
},
{
Name: aws.String("description"),
Values: []*string{aws.String("ELB " + name)},
},
},
})
if err != nil {
return resource.NonRetryableError(err)
}

niCount := len(out.NetworkInterfaces)
if niCount > 0 {
log.Printf("[DEBUG] Found %d ENIs to cleanup for NLB %q", niCount, lbArn)
return resource.RetryableError(fmt.Errorf("Waiting for %d ENIs of %q to clean up", niCount, lbArn))
}
log.Printf("[DEBUG] ENIs gone for NLB %q", lbArn)

return nil
})
}

func getLbNameFromArn(arn string) (string, error) {
re := regexp.MustCompile("([^/]+/[^/]+/[^/]+)$")
matches := re.FindStringSubmatch(arn)
if len(matches) != 2 {
return "", fmt.Errorf("Unexpected ARN format: %q", arn)
}

// e.g. app/example-alb/b26e625cdde161e6
return matches[1], nil
}

// flattenSubnetsFromAvailabilityZones creates a slice of strings containing the subnet IDs
// for the ALB based on the AvailabilityZones structure returned by the API.
func flattenSubnetsFromAvailabilityZones(availabilityZones []*elbv2.AvailabilityZone) []string {
Expand Down
79 changes: 79 additions & 0 deletions aws/resource_aws_lb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,33 @@ func TestAccAWSLB_networkLoadbalancer(t *testing.T) {
})
}

func TestAccAWSLB_networkLoadbalancerEIP(t *testing.T) {
var conf elbv2.LoadBalancer
lbName := fmt.Sprintf("testaccawslb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLBDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBConfig_networkLoadBalancerEIP(lbName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBExists("aws_lb.test", &conf),
resource.TestCheckResourceAttr("aws_lb.test", "name", lbName),
resource.TestCheckResourceAttr("aws_lb.test", "internal", "false"),
resource.TestCheckResourceAttr("aws_lb.test", "ip_address_type", "ipv4"),
resource.TestCheckResourceAttrSet("aws_lb.test", "zone_id"),
resource.TestCheckResourceAttrSet("aws_lb.test", "dns_name"),
resource.TestCheckResourceAttrSet("aws_lb.test", "arn"),
resource.TestCheckResourceAttr("aws_lb.test", "load_balancer_type", "network"),
resource.TestCheckResourceAttr("aws_lb.test", "subnet_mapping.#", "2"),
),
},
},
})
}

func TestAccAWSLBBackwardsCompatibility(t *testing.T) {
var conf elbv2.LoadBalancer
lbName := fmt.Sprintf("testaccawslb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
Expand Down Expand Up @@ -830,6 +857,58 @@ resource "aws_subnet" "alb_test" {
`, lbName)
}

func testAccAWSLBConfig_networkLoadBalancerEIP(lbName string) string {
return fmt.Sprintf(`
data "aws_availability_zones" "available" {}
resource "aws_vpc" "main" {
cidr_block = "10.10.0.0/16"
}
resource "aws_subnet" "public" {
count = "${length(data.aws_availability_zones.available.names)}"
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
cidr_block = "10.10.${count.index}.0/24"
vpc_id = "${aws_vpc.main.id}"
}
resource "aws_internet_gateway" "default" {
vpc_id = "${aws_vpc.main.id}"
}
resource "aws_route_table" "public" {
vpc_id = "${aws_vpc.main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.default.id}"
}
}
resource "aws_route_table_association" "a" {
count = "${length(data.aws_availability_zones.available.names)}"
subnet_id = "${aws_subnet.public.*.id[count.index]}"
route_table_id = "${aws_route_table.public.id}"
}
resource "aws_lb" "test" {
name = "%s"
load_balancer_type = "network"
subnet_mapping {
subnet_id = "${aws_subnet.public.0.id}"
allocation_id = "${aws_eip.lb.0.id}"
}
subnet_mapping {
subnet_id = "${aws_subnet.public.1.id}"
allocation_id = "${aws_eip.lb.1.id}"
}
}
resource "aws_eip" "lb" {
count = "${length(data.aws_availability_zones.available.names)}"
}
`, lbName)
}

func testAccAWSLBConfigBackwardsCompatibility(lbName string) string {
return fmt.Sprintf(`resource "aws_alb" "lb_test" {
name = "%s"
Expand Down

0 comments on commit 3056b04

Please sign in to comment.