Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws_dx_gateway_association: Allowed prefix support #8222

Merged
merged 2 commits into from
Apr 24, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 102 additions & 16 deletions aws/resource_aws_dx_gateway_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package aws
import (
"fmt"
"log"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
Expand All @@ -12,30 +13,47 @@ import (
)

const (
GatewayAssociationStateDeleted = "deleted"
gatewayAssociationStateDeleted = "deleted"
)

func resourceAwsDxGatewayAssociation() *schema.Resource {
return &schema.Resource{
Create: resourceAwsDxGatewayAssociationCreate,
Read: resourceAwsDxGatewayAssociationRead,
Update: resourceAwsDxGatewayAssociationUpdate,
Delete: resourceAwsDxGatewayAssociationDelete,
Importer: &schema.ResourceImporter{
State: resourceAwsDxGatewayAssociationImport,
},

Schema: map[string]*schema.Schema{
"allowed_prefixes": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is okay to mark this as Computed: true for now, given that it automatically includes the VPC CIDR if no prefixes are provided. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I think a missing call to resourceAwsDxGatewayAssociationRead() at the end of resourceAwsDxGatewayAssociationCreate() doesn't help either 😄.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ make testacc TEST=./aws/ TESTARGS='-run=TestAccAwsDxGatewayAssociation_basic'
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./aws/ -v -parallel 20 -run=TestAccAwsDxGatewayAssociation_basic -timeout 120m
=== RUN   TestAccAwsDxGatewayAssociation_basic
=== PAUSE TestAccAwsDxGatewayAssociation_basic
=== CONT  TestAccAwsDxGatewayAssociation_basic
--- PASS: TestAccAwsDxGatewayAssociation_basic (943.05s)
PASS
ok  	github.com/terraform-providers/terraform-provider-aws/aws	943.077s

Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"dx_gateway_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"vpn_gateway_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"dx_gateway_association_id": {
Type: schema.TypeString,
Computed: true,
},
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(15 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
}
Expand All @@ -47,14 +65,15 @@ func resourceAwsDxGatewayAssociationCreate(d *schema.ResourceData, meta interfac
dxgwId := d.Get("dx_gateway_id").(string)
vgwId := d.Get("vpn_gateway_id").(string)
req := &directconnect.CreateDirectConnectGatewayAssociationInput{
DirectConnectGatewayId: aws.String(dxgwId),
VirtualGatewayId: aws.String(vgwId),
AddAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(d.Get("allowed_prefixes").(*schema.Set)),
DirectConnectGatewayId: aws.String(dxgwId),
VirtualGatewayId: aws.String(vgwId),
}

log.Printf("[DEBUG] Creating Direct Connect gateway association: %#v", req)
_, err := conn.CreateDirectConnectGatewayAssociation(req)
if err != nil {
return fmt.Errorf("Error creating Direct Connect gateway association: %s", err)
return fmt.Errorf("error creating Direct Connect gateway association: %s", err)
}

d.SetId(dxGatewayAssociationId(dxgwId, vgwId))
Expand All @@ -69,7 +88,7 @@ func resourceAwsDxGatewayAssociationCreate(d *schema.ResourceData, meta interfac
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for Direct Connect gateway association (%s) to become available: %s", d.Id(), err)
return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to become available: %s", d.Id(), err)
}

return nil
Expand All @@ -80,45 +99,112 @@ func resourceAwsDxGatewayAssociationRead(d *schema.ResourceData, meta interface{

dxgwId := d.Get("dx_gateway_id").(string)
vgwId := d.Get("vpn_gateway_id").(string)
_, state, err := dxGatewayAssociationStateRefresh(conn, dxgwId, vgwId)()
assocRaw, state, err := dxGatewayAssociationStateRefresh(conn, dxgwId, vgwId)()
if err != nil {
return fmt.Errorf("Error reading Direct Connect gateway association: %s", err)
return fmt.Errorf("error reading Direct Connect gateway association: %s", err)
}
if state == GatewayAssociationStateDeleted {
if state == gatewayAssociationStateDeleted {
log.Printf("[WARN] Direct Connect gateway association (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

assoc := assocRaw.(*directconnect.GatewayAssociation)
d.Set("dx_gateway_id", assoc.DirectConnectGatewayId)
d.Set("vpn_gateway_id", assoc.VirtualGatewayId)
d.Set("dx_gateway_association_id", assoc.AssociationId)
err = d.Set("allowed_prefixes", flattenDxRouteFilterPrefixes(assoc.AllowedPrefixesToDirectConnectGateway))
if err != nil {
return fmt.Errorf("error setting allowed_prefixes: %s", err)
}

return nil
}

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

dxgwId := d.Get("dx_gateway_id").(string)
vgwId := d.Get("vpn_gateway_id").(string)

if d.HasChange("allowed_prefixes") {
oraw, nraw := d.GetChange("allowed_prefixes")
o := oraw.(*schema.Set)
n := nraw.(*schema.Set)
del := o.Difference(n)
add := n.Difference(o)

req := &directconnect.UpdateDirectConnectGatewayAssociationInput{
AddAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(add),
AssociationId: aws.String(d.Get("dx_gateway_association_id").(string)),
RemoveAllowedPrefixesToDirectConnectGateway: expandDxRouteFilterPrefixes(del),
}

log.Printf("[DEBUG] Direct Connect gateway association: %#v", req)
_, err := conn.UpdateDirectConnectGatewayAssociation(req)
if err != nil {
return fmt.Errorf("error updating Direct Connect gateway association (%s): %s", d.Id(), err)
}

stateConf := &resource.StateChangeConf{
Pending: []string{directconnect.GatewayAssociationStateUpdating},
Target: []string{directconnect.GatewayAssociationStateAssociated},
Refresh: dxGatewayAssociationStateRefresh(conn, dxgwId, vgwId),
Timeout: d.Timeout(schema.TimeoutUpdate),
Delay: 10 * time.Second,
MinTimeout: 5 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to become available: %s", d.Id(), err)
}
}

return resourceAwsDxGatewayAssociationRead(d, meta)
}

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

dxgwId := d.Get("dx_gateway_id").(string)
vgwId := d.Get("vpn_gateway_id").(string)

log.Printf("[DEBUG] Deleting Direct Connect gateway association: %s", d.Id())

_, err := conn.DeleteDirectConnectGatewayAssociation(&directconnect.DeleteDirectConnectGatewayAssociationInput{
DirectConnectGatewayId: aws.String(dxgwId),
VirtualGatewayId: aws.String(vgwId),
})
if isAWSErr(err, directconnect.ErrCodeClientException, "No association exists") {
return nil
}
if err != nil {
if isAWSErr(err, "DirectConnectClientException", "No association exists") {
return nil
}
return fmt.Errorf("Error deleting Direct Connect gateway association: %s", err)
return fmt.Errorf("error deleting Direct Connect gateway association: %s", err)
}

if err := waitForDirectConnectGatewayAssociationDeletion(conn, dxgwId, vgwId, d.Timeout(schema.TimeoutDelete)); err != nil {
return fmt.Errorf("Error waiting for Direct Connect gateway association (%s) to be deleted: %s", d.Id(), err.Error())
return fmt.Errorf("error waiting for Direct Connect gateway association (%s) to be deleted: %s", d.Id(), err.Error())
}

return nil
}

func resourceAwsDxGatewayAssociationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 2 {
return []*schema.ResourceData{}, fmt.Errorf("Wrong format of resource: %s. Please follow 'dx-gw-id/vgw-id'", d.Id())
}

dxgwId := parts[0]
vgwId := parts[1]
log.Printf("[DEBUG] Importing Direct Connect gateway association %s/%s", dxgwId, vgwId)

d.SetId(dxGatewayAssociationId(dxgwId, vgwId))
d.Set("dx_gateway_id", dxgwId)
d.Set("vpn_gateway_id", vgwId)

return []*schema.ResourceData{d}, nil
}

func dxGatewayAssociationStateRefresh(conn *directconnect.DirectConnect, dxgwId, vgwId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.DescribeDirectConnectGatewayAssociations(&directconnect.DescribeDirectConnectGatewayAssociationsInput{
Expand All @@ -132,7 +218,7 @@ func dxGatewayAssociationStateRefresh(conn *directconnect.DirectConnect, dxgwId,
n := len(resp.DirectConnectGatewayAssociations)
switch n {
case 0:
return "", GatewayAssociationStateDeleted, nil
return "", gatewayAssociationStateDeleted, nil

case 1:
assoc := resp.DirectConnectGatewayAssociations[0]
Expand All @@ -151,7 +237,7 @@ func dxGatewayAssociationId(dxgwId, vgwId string) string {
func waitForDirectConnectGatewayAssociationDeletion(conn *directconnect.DirectConnect, directConnectGatewayID, virtualGatewayID string, timeout time.Duration) error {
stateConf := &resource.StateChangeConf{
Pending: []string{directconnect.GatewayAssociationStateDisassociating},
Target: []string{directconnect.GatewayAssociationStateDisassociated, GatewayAssociationStateDeleted},
Target: []string{directconnect.GatewayAssociationStateDisassociated, gatewayAssociationStateDeleted},
Refresh: dxGatewayAssociationStateRefresh(conn, directConnectGatewayID, virtualGatewayID),
Timeout: timeout,
Delay: 10 * time.Second,
Expand Down
68 changes: 49 additions & 19 deletions aws/resource_aws_dx_gateway_association_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,59 @@ func testSweepDirectConnectGatewayAssociations(region string) error {
}

func TestAccAwsDxGatewayAssociation_basic(t *testing.T) {
resourceName := "aws_dx_gateway_association.test"
resourceNameDxGw := "aws_dx_gateway.test"
resourceNameVgw := "aws_vpn_gateway.test"
rName := fmt.Sprintf("terraform-testacc-dxgwassoc-%d", acctest.RandInt())
rBgpAsn := randIntRange(64512, 65534)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy,
Steps: []resource.TestStep{
{
Config: testAccDxGatewayAssociationConfig(acctest.RandString(5), randIntRange(64512, 65534)),
Config: testAccDxGatewayAssociationConfig(rName, rBgpAsn),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsDxGatewayAssociationExists("aws_dx_gateway_association.test"),
testAccCheckAwsDxGatewayAssociationExists(resourceName),
resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", resourceNameDxGw, "id"),
resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", resourceNameVgw, "id"),
),
},
{
ResourceName: resourceName,
ImportStateIdFunc: func(s *terraform.State) (string, error) {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return "", fmt.Errorf("Not Found: %s", resourceName)
}

return fmt.Sprintf("%s/%s", rs.Primary.Attributes["dx_gateway_id"], rs.Primary.Attributes["vpn_gateway_id"]), nil
},
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAwsDxGatewayAssociation_multiVgws(t *testing.T) {
resourceName1 := "aws_dx_gateway_association.test1"
resourceName2 := "aws_dx_gateway_association.test2"
rName1 := fmt.Sprintf("terraform-testacc-dxgwassoc-%d", acctest.RandInt())
rName2 := fmt.Sprintf("terraform-testacc-dxgwassoc-%d", acctest.RandInt())
rBgpAsn := randIntRange(64512, 65534)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsDxGatewayAssociationDestroy,
Steps: []resource.TestStep{
{
Config: testAccDxGatewayAssociationConfig_multiVgws(acctest.RandString(5), randIntRange(64512, 65534)),
Config: testAccDxGatewayAssociationConfig_multiVgws(rName1, rName2, rBgpAsn),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsDxGatewayAssociationExists("aws_dx_gateway_association.test1"),
testAccCheckAwsDxGatewayAssociationExists("aws_dx_gateway_association.test2"),
testAccCheckAwsDxGatewayAssociationExists(resourceName1),
testAccCheckAwsDxGatewayAssociationExists(resourceName2),
),
},
},
Expand Down Expand Up @@ -156,10 +183,13 @@ func testAccCheckAwsDxGatewayAssociationDestroy(s *terraform.State) error {

func testAccCheckAwsDxGatewayAssociationExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
_, ok := s.RootModule().Resources[name]
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

return nil
}
Expand All @@ -168,20 +198,20 @@ func testAccCheckAwsDxGatewayAssociationExists(name string) resource.TestCheckFu
func testAccDxGatewayAssociationConfig(rName string, rBgpAsn int) string {
return fmt.Sprintf(`
resource "aws_dx_gateway" "test" {
name = "terraform-testacc-dxgwassoc-%s"
amazon_side_asn = "%d"
name = %[1]q
amazon_side_asn = "%[2]d"
}

resource "aws_vpc" "test" {
cidr_block = "10.255.255.0/28"
tags = {
Name = "terraform-testacc-dxgwassoc-%s"
Name = %[1]q
}
}

resource "aws_vpn_gateway" "test" {
tags = {
Name = "terraform-testacc-dxgwassoc-%s"
Name = %[1]q
}
}

Expand All @@ -194,26 +224,26 @@ resource "aws_dx_gateway_association" "test" {
dx_gateway_id = "${aws_dx_gateway.test.id}"
vpn_gateway_id = "${aws_vpn_gateway_attachment.test.vpn_gateway_id}"
}
`, rName, rBgpAsn, rName, rName)
`, rName, rBgpAsn)
}

func testAccDxGatewayAssociationConfig_multiVgws(rName string, rBgpAsn int) string {
func testAccDxGatewayAssociationConfig_multiVgws(rName1, rName2 string, rBgpAsn int) string {
return fmt.Sprintf(`
resource "aws_dx_gateway" "test" {
name = "terraform-testacc-dxgwassoc-%s"
amazon_side_asn = "%d"
name = %[1]q
amazon_side_asn = "%[3]d"
}

resource "aws_vpc" "test1" {
cidr_block = "10.255.255.16/28"
tags = {
Name = "terraform-testacc-dxgwassoc-%s-1"
Name = %[1]q
}
}

resource "aws_vpn_gateway" "test1" {
tags = {
Name = "terraform-testacc-dxgwassoc-%s-1"
Name = %[1]q
}
}

Expand All @@ -230,13 +260,13 @@ resource "aws_dx_gateway_association" "test1" {
resource "aws_vpc" "test2" {
cidr_block = "10.255.255.32/28"
tags = {
Name = "terraform-testacc-dxgwassoc-%s-2"
Name = %[2]q
}
}

resource "aws_vpn_gateway" "test2" {
tags = {
Name = "terraform-testacc-dxgwassoc-%s-2"
Name = %[2]q
}
}

Expand All @@ -249,5 +279,5 @@ resource "aws_dx_gateway_association" "test2" {
dx_gateway_id = "${aws_dx_gateway.test.id}"
vpn_gateway_id = "${aws_vpn_gateway_attachment.test2.vpn_gateway_id}"
}
`, rName, rBgpAsn, rName, rName, rName, rName)
`, rName1, rName2, rBgpAsn)
}
Loading