Skip to content

Commit

Permalink
New Resource: aws_ec2_local_gateway_route (#13768)
Browse files Browse the repository at this point in the history
Reference: #13503

Output from acceptance testing:

```
--- PASS: TestAccAWSEc2LocalGatewayRoute_disappears (22.00s)
--- PASS: TestAccAWSEc2LocalGatewayRoute_basic (22.43s)
```
  • Loading branch information
bflad authored Jun 17, 2020
1 parent cc9eec4 commit fc485c8
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 1 deletion.
3 changes: 2 additions & 1 deletion aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,9 @@ func Provider() terraform.ResourceProvider {
"aws_ec2_capacity_reservation": resourceAwsEc2CapacityReservation(),
"aws_ec2_client_vpn_endpoint": resourceAwsEc2ClientVpnEndpoint(),
"aws_ec2_client_vpn_network_association": resourceAwsEc2ClientVpnNetworkAssociation(),
"aws_ec2_local_gateway_route_table_vpc_association": resourceAwsEc2LocalGatewayRouteTableVpcAssociation(),
"aws_ec2_fleet": resourceAwsEc2Fleet(),
"aws_ec2_local_gateway_route": resourceAwsEc2LocalGatewayRoute(),
"aws_ec2_local_gateway_route_table_vpc_association": resourceAwsEc2LocalGatewayRouteTableVpcAssociation(),
"aws_ec2_tag": resourceAwsEc2Tag(),
"aws_ec2_traffic_mirror_filter": resourceAwsEc2TrafficMirrorFilter(),
"aws_ec2_traffic_mirror_filter_rule": resourceAwsEc2TrafficMirrorFilterRule(),
Expand Down
205 changes: 205 additions & 0 deletions aws/resource_aws_ec2_local_gateway_route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package aws

import (
"fmt"
"log"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

const (
ec2LocalGatewayRouteEventualConsistencyTimeout = 1 * time.Minute
)

func resourceAwsEc2LocalGatewayRoute() *schema.Resource {
return &schema.Resource{
Create: resourceAwsEc2LocalGatewayRouteCreate,
Read: resourceAwsEc2LocalGatewayRouteRead,
Delete: resourceAwsEc2LocalGatewayRouteDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"destination_cidr_block": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateCIDRNetworkAddress,
},
"local_gateway_route_table_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"local_gateway_virtual_interface_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceAwsEc2LocalGatewayRouteCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

destination := d.Get("destination_cidr_block").(string)
localGatewayRouteTableID := d.Get("local_gateway_route_table_id").(string)

input := &ec2.CreateLocalGatewayRouteInput{
DestinationCidrBlock: aws.String(destination),
LocalGatewayRouteTableId: aws.String(localGatewayRouteTableID),
LocalGatewayVirtualInterfaceGroupId: aws.String(d.Get("local_gateway_virtual_interface_group_id").(string)),
}

_, err := conn.CreateLocalGatewayRoute(input)

if err != nil {
return fmt.Errorf("error creating EC2 Local Gateway Route: %s", err)
}

d.SetId(fmt.Sprintf("%s_%s", localGatewayRouteTableID, destination))

return resourceAwsEc2LocalGatewayRouteRead(d, meta)
}

func resourceAwsEc2LocalGatewayRouteRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

localGatewayRouteTableID, destination, err := decodeEc2LocalGatewayRouteID(d.Id())
if err != nil {
return err
}

var localGatewayRoute *ec2.LocalGatewayRoute
err = resource.Retry(ec2LocalGatewayRouteEventualConsistencyTimeout, func() *resource.RetryError {
var err error
localGatewayRoute, err = getEc2LocalGatewayRoute(conn, localGatewayRouteTableID, destination)

if err != nil {
return resource.NonRetryableError(err)
}

if d.IsNewResource() && localGatewayRoute == nil {
return resource.RetryableError(&resource.NotFoundError{})
}

return nil
})

if isResourceTimeoutError(err) {
localGatewayRoute, err = getEc2LocalGatewayRoute(conn, localGatewayRouteTableID, destination)
}

if isAWSErr(err, "InvalidRouteTableID.NotFound", "") {
log.Printf("[WARN] EC2 Local Gateway Route Table (%s) not found, removing from state", localGatewayRouteTableID)
d.SetId("")
return nil
}

if !d.IsNewResource() && isResourceNotFoundError(err) {
log.Printf("[WARN] EC2 Local Gateway Route (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading EC2 Local Gateway Route: %s", err)
}

if localGatewayRoute == nil {
log.Printf("[WARN] EC2 Local Gateway Route (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

state := aws.StringValue(localGatewayRoute.State)
if state == ec2.LocalGatewayRouteStateDeleted || state == ec2.LocalGatewayRouteStateDeleting {
log.Printf("[WARN] EC2 Local Gateway Route (%s) deleted, removing from state", d.Id())
d.SetId("")
return nil
}

d.Set("destination_cidr_block", localGatewayRoute.DestinationCidrBlock)
d.Set("local_gateway_virtual_interface_group_id", localGatewayRoute.LocalGatewayVirtualInterfaceGroupId)
d.Set("local_gateway_route_table_id", localGatewayRoute.LocalGatewayRouteTableId)

return nil
}

func resourceAwsEc2LocalGatewayRouteDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

localGatewayRouteTableID, destination, err := decodeEc2LocalGatewayRouteID(d.Id())
if err != nil {
return err
}

input := &ec2.DeleteLocalGatewayRouteInput{
DestinationCidrBlock: aws.String(destination),
LocalGatewayRouteTableId: aws.String(localGatewayRouteTableID),
}

log.Printf("[DEBUG] Deleting EC2 Local Gateway Route (%s): %s", d.Id(), input)
_, err = conn.DeleteLocalGatewayRoute(input)

if isAWSErr(err, "InvalidRoute.NotFound", "") || isAWSErr(err, "InvalidRouteTableID.NotFound", "") {
return nil
}

if err != nil {
return fmt.Errorf("error deleting EC2 Local Gateway Route: %s", err)
}

return nil
}

func decodeEc2LocalGatewayRouteID(id string) (string, string, error) {
parts := strings.Split(id, "_")

if len(parts) != 2 {
return "", "", fmt.Errorf("Unexpected format of ID (%q), expected tgw-rtb-ID_DESTINATION", id)
}

return parts[0], parts[1], nil
}

func getEc2LocalGatewayRoute(conn *ec2.EC2, localGatewayRouteTableID, destination string) (*ec2.LocalGatewayRoute, error) {
input := &ec2.SearchLocalGatewayRoutesInput{
Filters: []*ec2.Filter{
{
Name: aws.String("type"),
Values: aws.StringSlice([]string{"static"}),
},
},
LocalGatewayRouteTableId: aws.String(localGatewayRouteTableID),
}

output, err := conn.SearchLocalGatewayRoutes(input)

if err != nil {
return nil, err
}

if output == nil || len(output.Routes) == 0 {
return nil, nil
}

for _, route := range output.Routes {
if route == nil {
continue
}

if aws.StringValue(route.DestinationCidrBlock) == destination {
return route, nil
}
}

return nil, nil
}
167 changes: 167 additions & 0 deletions aws/resource_aws_ec2_local_gateway_route_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package aws

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccAWSEc2LocalGatewayRoute_basic(t *testing.T) {
// Hide Outposts testing behind consistent environment variable
outpostArn := os.Getenv("AWS_OUTPOST_ARN")
if outpostArn == "" {
t.Skip(
"Environment variable AWS_OUTPOST_ARN is not set. " +
"This environment variable must be set to the ARN of " +
"a deployed Outpost to enable this test.")
}

rInt := randIntRange(0, 255)
destinationCidrBlock := fmt.Sprintf("172.16.%d.0/24", rInt)
localGatewayRouteTableDataSourceName := "data.aws_ec2_local_gateway_route_table.test"
localGatewayVirtualInterfaceGroupDataSourceName := "data.aws_ec2_local_gateway_virtual_interface_group.test"
resourceName := "aws_ec2_local_gateway_route.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEc2LocalGatewayRouteDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2LocalGatewayRouteConfigDestinationCidrBlock(destinationCidrBlock),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEc2LocalGatewayRouteExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "destination_cidr_block", destinationCidrBlock),
resource.TestCheckResourceAttrPair(resourceName, "local_gateway_route_table_id", localGatewayRouteTableDataSourceName, "id"),
resource.TestCheckResourceAttrPair(resourceName, "local_gateway_virtual_interface_group_id", localGatewayVirtualInterfaceGroupDataSourceName, "id"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAWSEc2LocalGatewayRoute_disappears(t *testing.T) {
// Hide Outposts testing behind consistent environment variable
outpostArn := os.Getenv("AWS_OUTPOST_ARN")
if outpostArn == "" {
t.Skip(
"Environment variable AWS_OUTPOST_ARN is not set. " +
"This environment variable must be set to the ARN of " +
"a deployed Outpost to enable this test.")
}

rInt := randIntRange(0, 255)
destinationCidrBlock := fmt.Sprintf("172.16.%d.0/24", rInt)
resourceName := "aws_ec2_local_gateway_route.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEc2LocalGatewayRouteDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2LocalGatewayRouteConfigDestinationCidrBlock(destinationCidrBlock),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEc2LocalGatewayRouteExists(resourceName),
testAccCheckResourceDisappears(testAccProvider, resourceAwsEc2LocalGatewayRoute(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckAWSEc2LocalGatewayRouteExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No EC2 Local Gateway Route ID is set")
}

localGatewayRouteTableID, destination, err := decodeEc2LocalGatewayRouteID(rs.Primary.ID)

if err != nil {
return err
}

conn := testAccProvider.Meta().(*AWSClient).ec2conn

route, err := getEc2LocalGatewayRoute(conn, localGatewayRouteTableID, destination)

if err != nil {
return err
}

if route == nil {
return fmt.Errorf("EC2 Local Gateway Route (%s) not found", rs.Primary.ID)
}

return nil
}
}

func testAccCheckAWSEc2LocalGatewayRouteDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_ec2_local_gateway_route" {
continue
}

localGatewayRouteTableID, destination, err := decodeEc2LocalGatewayRouteID(rs.Primary.ID)

if err != nil {
return err
}

route, err := getEc2LocalGatewayRoute(conn, localGatewayRouteTableID, destination)

if isAWSErr(err, "InvalidRouteTableID.NotFound", "") {
continue
}

if err != nil {
return err
}

if route == nil {
continue
}

return fmt.Errorf("EC2 Local Gateway Route (%s) still exists", rs.Primary.ID)
}

return nil
}

func testAccAWSEc2LocalGatewayRouteConfigDestinationCidrBlock(destinationCidrBlock string) string {
return fmt.Sprintf(`
data "aws_ec2_local_gateways" "test" {}
data "aws_ec2_local_gateway_route_table" "test" {
local_gateway_id = tolist(data.aws_ec2_local_gateways.test.ids)[0]
}
data "aws_ec2_local_gateway_virtual_interface_group" "test" {
local_gateway_id = tolist(data.aws_ec2_local_gateways.test.ids)[0]
}
resource "aws_ec2_local_gateway_route" "test" {
destination_cidr_block = %[1]q
local_gateway_route_table_id = data.aws_ec2_local_gateway_route_table.test.id
local_gateway_virtual_interface_group_id = data.aws_ec2_local_gateway_virtual_interface_group.test.id
}
`, destinationCidrBlock)
}
3 changes: 3 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,9 @@
<li>
<a href="/docs/providers/aws/r/ec2_client_vpn_network_association.html">aws_ec2_client_vpn_network_association</a>
</li>
<li>
<a href="/docs/providers/aws/r/ec2_local_gateway_route.html">aws_ec2_local_gateway_route</a>
</li>
<li>
<a href="/docs/providers/aws/r/ec2_local_gateway_route_table_vpc_association.html">aws_ec2_local_gateway_route_table_vpc_association</a>
</li>
Expand Down
Loading

0 comments on commit fc485c8

Please sign in to comment.