From 6a9b932bc0dbc6ce878f51d6738fb0feb1385d95 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 3 May 2019 11:27:42 -0400 Subject: [PATCH 01/10] Add 'aws_dx_transit_virtual_interface' resource. --- aws/dx_vif.go | 25 +- aws/provider.go | 1 + ...source_aws_dx_transit_virtual_interface.go | 228 ++++++++++++++++++ ...e_aws_dx_transit_virtual_interface_test.go | 140 +++++++++++ website/aws.erb | 3 + ...dx_transit_virtual_interface.html.markdown | 74 ++++++ 6 files changed, 456 insertions(+), 15 deletions(-) create mode 100644 aws/resource_aws_dx_transit_virtual_interface.go create mode 100644 aws/resource_aws_dx_transit_virtual_interface_test.go create mode 100644 website/docs/r/dx_transit_virtual_interface.html.markdown diff --git a/aws/dx_vif.go b/aws/dx_vif.go index ebf0a54e045f..e610c38f5b7c 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -14,7 +14,7 @@ import ( func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*directconnect.VirtualInterface, error) { resp, state, err := dxVirtualInterfaceStateRefresh(conn, id)() if err != nil { - return nil, fmt.Errorf("Error reading Direct Connect virtual interface: %s", err) + return nil, fmt.Errorf("error reading Direct Connect virtual interface (%s): %s", id, err) } if state == directconnect.VirtualInterfaceStateDeleted { return nil, nil @@ -26,26 +26,21 @@ func dxVirtualInterfaceRead(id string, conn *directconnect.DirectConnect) (*dire func dxVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - req := &directconnect.UpdateVirtualInterfaceAttributesInput{ - VirtualInterfaceId: aws.String(d.Id()), - } - - requestUpdate := false if d.HasChange("mtu") { - req.Mtu = aws.Int64(int64(d.Get("mtu").(int))) - requestUpdate = true - } + req := &directconnect.UpdateVirtualInterfaceAttributesInput{ + Mtu: aws.Int64(int64(d.Get("mtu").(int))), + VirtualInterfaceId: aws.String(d.Id()), + } - if requestUpdate { log.Printf("[DEBUG] Modifying Direct Connect virtual interface attributes: %#v", req) _, err := conn.UpdateVirtualInterfaceAttributes(req) if err != nil { - return fmt.Errorf("Error modifying Direct Connect virtual interface (%s) attributes, error: %s", d.Id(), err) + return fmt.Errorf("error modifying Direct Connect virtual interface (%s) attributes, error: %s", d.Id(), err) } } if err := setTagsDX(conn, d, d.Get("arn").(string)); err != nil { - return err + return fmt.Errorf("error setting Direct Connect virtual interface (%s) tags: %s", d.Id(), err) } return nil @@ -62,7 +57,7 @@ func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { return nil } - return fmt.Errorf("Error deleting Direct Connect virtual interface: %s", err) + return fmt.Errorf("error deleting Direct Connect virtual interface (%s): %s", d.Id(), err) } deleteStateConf := &resource.StateChangeConf{ @@ -85,7 +80,7 @@ func dxVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { } _, err = deleteStateConf.WaitForState() if err != nil { - return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to be deleted: %s", d.Id(), err) + return fmt.Errorf("error waiting for Direct Connect virtual interface (%s) to be deleted: %s", d.Id(), err) } return nil @@ -125,7 +120,7 @@ func dxVirtualInterfaceWaitUntilAvailable(conn *directconnect.DirectConnect, vif MinTimeout: 5 * time.Second, } if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf("Error waiting for Direct Connect virtual interface (%s) to become available: %s", vifId, err) + return fmt.Errorf("error waiting for Direct Connect virtual interface (%s) to become available: %s", vifId, err) } return nil diff --git a/aws/provider.go b/aws/provider.go index 802c49b1dc5d..a9efe4b41ed2 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -441,6 +441,7 @@ func Provider() terraform.ResourceProvider { "aws_dx_lag": resourceAwsDxLag(), "aws_dx_private_virtual_interface": resourceAwsDxPrivateVirtualInterface(), "aws_dx_public_virtual_interface": resourceAwsDxPublicVirtualInterface(), + "aws_dx_transit_virtual_interface": resourceAwsDxTransitVirtualInterface(), "aws_dynamodb_table": resourceAwsDynamoDbTable(), "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go new file mode 100644 index 000000000000..9f7dab01563e --- /dev/null +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -0,0 +1,228 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsDxTransitVirtualInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxTransitVirtualInterfaceCreate, + Read: resourceAwsDxTransitVirtualInterfaceRead, + Update: resourceAwsDxTransitVirtualInterfaceUpdate, + Delete: resourceAwsDxTransitVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsDxTransitVirtualInterfaceImport, + }, + + Schema: map[string]*schema.Schema{ + "address_family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), + }, + "amazon_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "bgp_auth_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "customer_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "dx_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "jumbo_frame_capable": { + Type: schema.TypeBool, + Computed: true, + }, + "mtu": { + Type: schema.TypeInt, + Default: 1500, + Optional: true, + ValidateFunc: validation.IntInSlice([]int{1500, 8500}), + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "tags": tagsSchema(), + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + } +} + +func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + req := &directconnect.CreateTransitVirtualInterfaceInput{ + ConnectionId: aws.String(d.Get("connection_id").(string)), + NewTransitVirtualInterface: &directconnect.NewTransitVirtualInterface{ + AddressFamily: aws.String(d.Get("address_family").(string)), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), + DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), + Mtu: aws.Int64(int64(d.Get("mtu").(int))), + VirtualInterfaceName: aws.String(d.Get("name").(string)), + Vlan: aws.Int64(int64(d.Get("vlan").(int))), + }, + } + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewTransitVirtualInterface.AmazonAddress = aws.String(v.(string)) + } + if v, ok := d.GetOk("bgp_auth_key"); ok { + req.NewTransitVirtualInterface.AuthKey = aws.String(v.(string)) + } + if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { + req.NewTransitVirtualInterface.CustomerAddress = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating Direct Connect transit virtual interface: %#v", req) + resp, err := conn.CreateTransitVirtualInterface(req) + if err != nil { + return fmt.Errorf("error creating Direct Connect transit virtual interface: %s", err) + } + + d.SetId(aws.StringValue(resp.VirtualInterface.VirtualInterfaceId)) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + if err := dxTransitVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return err + } + + return resourceAwsDxTransitVirtualInterfaceUpdate(d, meta) +} + +func resourceAwsDxTransitVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return err + } + if vif == nil { + log.Printf("[WARN] Direct Connect transit virtual interface (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("address_family", vif.AddressFamily) + d.Set("amazon_address", vif.AmazonAddress) + d.Set("aws_device", vif.AwsDeviceV2) + d.Set("bgp_asn", vif.Asn) + d.Set("bgp_auth_key", vif.AuthKey) + d.Set("connection_id", vif.ConnectionId) + d.Set("customer_address", vif.CustomerAddress) + d.Set("dx_gateway_id", vif.DirectConnectGatewayId) + d.Set("jumbo_frame_capable", vif.JumboFrameCapable) + d.Set("mtu", vif.Mtu) + d.Set("name", vif.VirtualInterfaceName) + d.Set("vlan", vif.Vlan) + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect transit virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil +} + +func resourceAwsDxTransitVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { + if err := dxVirtualInterfaceUpdate(d, meta); err != nil { + return err + } + + if err := dxTransitVirtualInterfaceWaitUntilAvailable(meta.(*AWSClient).dxconn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil { + return err + } + + return resourceAwsDxTransitVirtualInterfaceRead(d, meta) +} + +func resourceAwsDxTransitVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { + return dxVirtualInterfaceDelete(d, meta) +} + +func resourceAwsDxTransitVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return []*schema.ResourceData{d}, nil +} + +func dxTransitVirtualInterfaceWaitUntilAvailable(conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { + return dxVirtualInterfaceWaitUntilAvailable( + conn, + vifId, + timeout, + []string{ + directconnect.VirtualInterfaceStatePending, + }, + []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateDown, + }) +} diff --git a/aws/resource_aws_dx_transit_virtual_interface_test.go b/aws/resource_aws_dx_transit_virtual_interface_test.go new file mode 100644 index 000000000000..140fdc479dee --- /dev/null +++ b/aws/resource_aws_dx_transit_virtual_interface_test.go @@ -0,0 +1,140 @@ +package aws + +import ( + "fmt" + "os" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsDxTransitVirtualInterface_basic(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + + resourceName := "aws_dx_transit_virtual_interface.test" + rName := fmt.Sprintf("tf-testacc-transit-vif-%s", acctest.RandString(9)) + amzAsn := randIntRange(64512, 65534) + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxTransitVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + ), + }, + { + Config: testAccDxTransitVirtualInterfaceConfig_updated(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Environment", "test"), + resource.TestCheckResourceAttr(resourceName, "mtu", "8500"), + ), + }, + // Test import. + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsDxTransitVirtualInterfaceDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).dxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_dx_transit_virtual_interface" { + continue + } + + input := &directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(rs.Primary.ID), + } + + resp, err := conn.DescribeVirtualInterfaces(input) + if err != nil { + return err + } + for _, v := range resp.VirtualInterfaces { + if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { + return fmt.Errorf("[DESTROY ERROR] Dx Transit VIF (%s) not deleted", rs.Primary.ID) + } + } + } + return nil +} + +func testAccCheckAwsDxTransitVirtualInterfaceExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +func testAccDxTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return fmt.Sprintf(` +resource "aws_dx_gateway" "test" { + name = %[2]q + amazon_side_asn = %[3]d +} + +resource "aws_dx_transit_virtual_interface" "test" { + connection_id = %[1]q + + dx_gateway_id = "${aws_dx_gateway.test.id}" + name = %[2]q + vlan = %[5]d + address_family = "ipv4" + bgp_asn = %[4]d +} +`, cid, rName, amzAsn, bgpAsn, vlan) +} + +func testAccDxTransitVirtualInterfaceConfig_updated(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return fmt.Sprintf(` +resource "aws_dx_gateway" "test" { + name = %[2]q + amazon_side_asn = %[3]d +} + +resource "aws_dx_transit_virtual_interface" "test" { + connection_id = %[1]q + + dx_gateway_id = "${aws_dx_gateway.test.id}" + name = %[2]q + vlan = %[5]d + address_family = "ipv4" + bgp_asn = %[4]d + mtu = 8500 + + tags = { + Environment = "test" + } +} +`, cid, rName, amzAsn, bgpAsn, vlan) +} diff --git a/website/aws.erb b/website/aws.erb index eae02116af9a..0590ef7616b0 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -876,6 +876,9 @@
  • aws_dx_public_virtual_interface
  • +
  • + aws_dx_transit_virtual_interface +
  • diff --git a/website/docs/r/dx_transit_virtual_interface.html.markdown b/website/docs/r/dx_transit_virtual_interface.html.markdown new file mode 100644 index 000000000000..0a2844cd2902 --- /dev/null +++ b/website/docs/r/dx_transit_virtual_interface.html.markdown @@ -0,0 +1,74 @@ +--- +layout: "aws" +page_title: "AWS: aws_dx_transit_virtual_interface" +sidebar_current: "docs-aws-resource-dx-transit-virtual-interface" +description: |- + Provides a Direct Connect transit virtual interface resource. +--- + +# Resource: aws_dx_transit_virtual_interface + +Provides a Direct Connect transit virtual interface resource. +A transit virtual interface is a VLAN that transports traffic from a [Direct Connect gateway](dx_gateway.html) to one or more [transit gateways](ec2_transit_gateway.html). + +## Example Usage + +```hcl +resource "aws_dx_gateway" "example" { + name = "tf-dxg-example" + amazon_side_asn = 64512 +} + +resource "aws_dx_transit_virtual_interface" "example" { + connection_id = "dxcon-zzzzzzzz" + + dx_gateway_id = "${aws_dx_gateway.example.id}" + name = "tf-transit-vif-example" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `address_family` - (Required) The address family for the BGP peer. `ipv4 ` or `ipv6`. +* `bgp_asn` - (Required) The autonomous system (AS) number for Border Gateway Protocol (BGP) configuration. +* `connection_id` - (Required) The ID of the Direct Connect connection (or LAG) on which to create the virtual interface. +* `dx_gateway_id` - (Required) The ID of the Direct Connect gateway to which to connect the virtual interface. +* `name` - (Required) The name for the virtual interface. +* `vlan` - (Required) The VLAN ID. +* `amazon_address` - (Optional) The IPv4 CIDR address to use to send traffic to Amazon. Required for IPv4 BGP peers. +* `bgp_auth_key` - (Optional) The authentication key for BGP configuration. +* `customer_address` - (Optional) The IPv4 CIDR destination address to which Amazon should send traffic. Required for IPv4 BGP peers. +* `mtu` - (Optional) The maximum transmission unit (MTU) is the size, in bytes, of the largest permissible packet that can be passed over the connection. +The MTU of a virtual transit interface can be either `1500` or `8500` (jumbo frames). Default is `1500`. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the virtual interface. +* `arn` - The ARN of the virtual interface. +* `aws_device` - The Direct Connect endpoint on which the virtual interface terminates. +* `jumbo_frame_capable` - Indicates whether jumbo frames (9001 MTU) are supported. + +## Timeouts + +`aws_dx_transit_virtual_interface` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating virtual interface +- `update` - (Default `10 minutes`) Used for virtual interface modifications +- `delete` - (Default `10 minutes`) Used for destroying virtual interface + +## Import + +Direct Connect transit virtual interfaces can be imported using the `vif id`, e.g. + +``` +$ terraform import aws_dx_transit_virtual_interface.test dxvif-33cc44dd +``` From 68c29942ac9f87054f3c9455880f26f53c91408c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 7 May 2019 14:51:33 -0400 Subject: [PATCH 02/10] Documentation fix after review. --- website/docs/r/dx_transit_virtual_interface.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/dx_transit_virtual_interface.html.markdown b/website/docs/r/dx_transit_virtual_interface.html.markdown index 0a2844cd2902..59052d1afc11 100644 --- a/website/docs/r/dx_transit_virtual_interface.html.markdown +++ b/website/docs/r/dx_transit_virtual_interface.html.markdown @@ -54,7 +54,7 @@ In addition to all arguments above, the following attributes are exported: * `id` - The ID of the virtual interface. * `arn` - The ARN of the virtual interface. * `aws_device` - The Direct Connect endpoint on which the virtual interface terminates. -* `jumbo_frame_capable` - Indicates whether jumbo frames (9001 MTU) are supported. +* `jumbo_frame_capable` - Indicates whether jumbo frames (8500 MTU) are supported. ## Timeouts From 0528415a757e98cda6a9cfac2d0a54f4e79d2cde Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 29 Jul 2019 13:26:05 -0400 Subject: [PATCH 03/10] Update website/docs/r/dx_transit_virtual_interface.html.markdown Co-Authored-By: Brian Flad --- website/docs/r/dx_transit_virtual_interface.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/dx_transit_virtual_interface.html.markdown b/website/docs/r/dx_transit_virtual_interface.html.markdown index 59052d1afc11..9ffea47fed48 100644 --- a/website/docs/r/dx_transit_virtual_interface.html.markdown +++ b/website/docs/r/dx_transit_virtual_interface.html.markdown @@ -20,7 +20,7 @@ resource "aws_dx_gateway" "example" { } resource "aws_dx_transit_virtual_interface" "example" { - connection_id = "dxcon-zzzzzzzz" + connection_id = "${aws_dx_connection.example.id}" dx_gateway_id = "${aws_dx_gateway.example.id}" name = "tf-transit-vif-example" From 630edbd3aa9b7c4e6c8470f6ecdf96fcd00b2337 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 29 Jul 2019 14:11:14 -0400 Subject: [PATCH 04/10] Code changes after review. --- ...source_aws_dx_transit_virtual_interface.go | 3 +- ...e_aws_dx_transit_virtual_interface_test.go | 39 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go index 9f7dab01563e..916239e1ba4a 100644 --- a/aws/resource_aws_dx_transit_virtual_interface.go +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -115,6 +115,7 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), Mtu: aws.Int64(int64(d.Get("mtu").(int))), + Tags: tagsFromMapDX(d.Get("tags").(map[string]interface{})), VirtualInterfaceName: aws.String(d.Get("name").(string)), Vlan: aws.Int64(int64(d.Get("vlan").(int))), }, @@ -149,7 +150,7 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int return err } - return resourceAwsDxTransitVirtualInterfaceUpdate(d, meta) + return resourceAwsDxTransitVirtualInterfaceRead(d, meta) } func resourceAwsDxTransitVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { diff --git a/aws/resource_aws_dx_transit_virtual_interface_test.go b/aws/resource_aws_dx_transit_virtual_interface_test.go index 140fdc479dee..275308bfc4b5 100644 --- a/aws/resource_aws_dx_transit_virtual_interface_test.go +++ b/aws/resource_aws_dx_transit_virtual_interface_test.go @@ -67,30 +67,55 @@ func testAccCheckAwsDxTransitVirtualInterfaceDestroy(s *terraform.State) error { if rs.Type != "aws_dx_transit_virtual_interface" { continue } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } - input := &directconnect.DescribeVirtualInterfacesInput{ + resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ VirtualInterfaceId: aws.String(rs.Primary.ID), + }) + if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { + continue } - - resp, err := conn.DescribeVirtualInterfaces(input) if err != nil { return err } - for _, v := range resp.VirtualInterfaces { - if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Transit VIF (%s) not deleted", rs.Primary.ID) + + n := len(resp.VirtualInterfaces) + switch n { + case 0: + continue + case 1: + if aws.StringValue(resp.VirtualInterfaces[0].VirtualInterfaceState) == directconnect.VirtualInterfaceStateDeleted { + continue } + return fmt.Errorf("still exist.") + default: + return fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, rs.Primary.ID) } } + return nil } func testAccCheckAwsDxTransitVirtualInterfaceExists(name string) resource.TestCheckFunc { return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] + conn := testAccProvider.Meta().(*AWSClient).dxconn + + 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") + } + + _, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } return nil } From 085be406e2d3c26da4458342b141aea4e733a6fe Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 29 Jul 2019 16:11:15 -0400 Subject: [PATCH 05/10] Ensure correct VIF type during import of Direct Connect Virtual Interfaces - #8843. --- aws/resource_aws_dx_transit_virtual_interface.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go index 916239e1ba4a..e068d0c2c466 100644 --- a/aws/resource_aws_dx_transit_virtual_interface.go +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -202,6 +202,20 @@ func resourceAwsDxTransitVirtualInterfaceDelete(d *schema.ResourceData, meta int } func resourceAwsDxTransitVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "transit" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } + arn := arn.ARN{ Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, From 405b5d6d235bf565e78ec838b395ae3446730fa6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 30 Jul 2019 10:19:50 -0400 Subject: [PATCH 06/10] Fix: Error: error creating Direct Connect transit virtual interface: InvalidParameter: 1 validation error(s) found. - minimum field size of 1, CreateTransitVirtualInterfaceInput.NewTransitVirtualInterface.Tags. --- aws/resource_aws_dx_transit_virtual_interface.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go index e068d0c2c466..d7ed9903e058 100644 --- a/aws/resource_aws_dx_transit_virtual_interface.go +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -115,7 +115,6 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), Mtu: aws.Int64(int64(d.Get("mtu").(int))), - Tags: tagsFromMapDX(d.Get("tags").(map[string]interface{})), VirtualInterfaceName: aws.String(d.Get("name").(string)), Vlan: aws.Int64(int64(d.Get("vlan").(int))), }, @@ -129,6 +128,9 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { req.NewTransitVirtualInterface.CustomerAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("tags"); ok { + req.NewTransitVirtualInterface.Tags = tagsFromMapDX(v.(map[string]interface{})) + } log.Printf("[DEBUG] Creating Direct Connect transit virtual interface: %#v", req) resp, err := conn.CreateTransitVirtualInterface(req) From 5a60769895be0a499d2d3d44624b049ba4e5265f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 30 Jul 2019 11:20:42 -0400 Subject: [PATCH 07/10] '%#v' -> '%s' when logging request structure. --- aws/dx_vif.go | 2 +- aws/resource_aws_dx_transit_virtual_interface.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/dx_vif.go b/aws/dx_vif.go index e610c38f5b7c..21de7e8689c9 100644 --- a/aws/dx_vif.go +++ b/aws/dx_vif.go @@ -32,7 +32,7 @@ func dxVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { VirtualInterfaceId: aws.String(d.Id()), } - log.Printf("[DEBUG] Modifying Direct Connect virtual interface attributes: %#v", req) + log.Printf("[DEBUG] Modifying Direct Connect virtual interface attributes: %s", req) _, err := conn.UpdateVirtualInterfaceAttributes(req) if err != nil { return fmt.Errorf("error modifying Direct Connect virtual interface (%s) attributes, error: %s", d.Id(), err) diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go index d7ed9903e058..017c6a3e678e 100644 --- a/aws/resource_aws_dx_transit_virtual_interface.go +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -132,7 +132,7 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int req.NewTransitVirtualInterface.Tags = tagsFromMapDX(v.(map[string]interface{})) } - log.Printf("[DEBUG] Creating Direct Connect transit virtual interface: %#v", req) + log.Printf("[DEBUG] Creating Direct Connect transit virtual interface: %s", req) resp, err := conn.CreateTransitVirtualInterface(req) if err != nil { return fmt.Errorf("error creating Direct Connect transit virtual interface: %s", err) From aee8c35df868a5ec59c45e72544b119bf1e9a787 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 30 Jul 2019 15:01:32 -0400 Subject: [PATCH 08/10] Run acceptance tests serially: 'Only one Transit Virtual Interface is allowed on a Connection'. --- ...e_aws_dx_transit_virtual_interface_test.go | 213 +++++++++++++++--- 1 file changed, 182 insertions(+), 31 deletions(-) diff --git a/aws/resource_aws_dx_transit_virtual_interface_test.go b/aws/resource_aws_dx_transit_virtual_interface_test.go index 275308bfc4b5..bc929bdff26b 100644 --- a/aws/resource_aws_dx_transit_virtual_interface_test.go +++ b/aws/resource_aws_dx_transit_virtual_interface_test.go @@ -3,6 +3,8 @@ package aws import ( "fmt" "os" + "regexp" + "strconv" "testing" "github.com/aws/aws-sdk-go/aws" @@ -12,20 +14,36 @@ import ( "github.com/hashicorp/terraform/terraform" ) -func TestAccAwsDxTransitVirtualInterface_basic(t *testing.T) { +func TestAccAwsDxTransitVirtualInterface(t *testing.T) { + testCases := map[string]func(t *testing.T){ + "basic": testAccAwsDxTransitVirtualInterface_basic, + "tags": testAccAwsDxTransitVirtualInterface_Tags, + } + + for name, tc := range testCases { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccAwsDxTransitVirtualInterface_basic(t *testing.T) { key := "DX_CONNECTION_ID" connectionId := os.Getenv(key) if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } + var vif directconnect.VirtualInterface resourceName := "aws_dx_transit_virtual_interface.test" + dxGatewayResourceName := "aws_dx_gateway.test" rName := fmt.Sprintf("tf-testacc-transit-vif-%s", acctest.RandString(9)) amzAsn := randIntRange(64512, 65534) bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsDxTransitVirtualInterfaceDestroy, @@ -33,21 +51,117 @@ func TestAccAwsDxTransitVirtualInterface_basic(t *testing.T) { { Config: testAccDxTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName), + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), - resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, { Config: testAccDxTransitVirtualInterfaceConfig_updated(connectionId, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.Environment", "test"), + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), resource.TestCheckResourceAttr(resourceName, "mtu", "8500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + ), + }, + // Test import. + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsDxTransitVirtualInterface_Tags(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_transit_virtual_interface.test" + dxGatewayResourceName := "aws_dx_gateway.test" + rName := fmt.Sprintf("tf-testacc-transit-vif-%s", acctest.RandString(9)) + amzAsn := randIntRange(64512, 65534) + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxTransitVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxTransitVirtualInterfaceConfig_tags(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + ), + }, + { + Config: testAccDxTransitVirtualInterfaceConfig_tagsUpdated(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxTransitVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(resourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, // Test import. @@ -98,7 +212,7 @@ func testAccCheckAwsDxTransitVirtualInterfaceDestroy(s *terraform.State) error { return nil } -func testAccCheckAwsDxTransitVirtualInterfaceExists(name string) resource.TestCheckFunc { +func testAccCheckAwsDxTransitVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).dxconn @@ -110,56 +224,93 @@ func testAccCheckAwsDxTransitVirtualInterfaceExists(name string) resource.TestCh return fmt.Errorf("No ID is set") } - _, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ + resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ VirtualInterfaceId: aws.String(rs.Primary.ID), }) if err != nil { return err } + if n := len(resp.VirtualInterfaces); n != 1 { + return fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, rs.Primary.ID) + } + + *vif = *resp.VirtualInterfaces[0] + return nil } } -func testAccDxTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { +func testAccDxTransitVirtualInterfaceConfig_base(rName string, amzAsn int) string { return fmt.Sprintf(` resource "aws_dx_gateway" "test" { - name = %[2]q - amazon_side_asn = %[3]d + name = %[1]q + amazon_side_asn = %[2]d +} +`, rName, amzAsn) } +func testAccDxTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { - connection_id = %[1]q - + address_family = "ipv4" + bgp_asn = %[3]d dx_gateway_id = "${aws_dx_gateway.test.id}" + connection_id = %[1]q name = %[2]q - vlan = %[5]d - address_family = "ipv4" - bgp_asn = %[4]d + vlan = %[4]d } -`, cid, rName, amzAsn, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan) } func testAccDxTransitVirtualInterfaceConfig_updated(cid, rName string, amzAsn, bgpAsn, vlan int) string { - return fmt.Sprintf(` -resource "aws_dx_gateway" "test" { - name = %[2]q - amazon_side_asn = %[3]d + return testAccDxTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` +resource "aws_dx_transit_virtual_interface" "test" { + address_family = "ipv4" + bgp_asn = %[3]d + dx_gateway_id = "${aws_dx_gateway.test.id}" + connection_id = %[1]q + mtu = 8500 + name = %[2]q + vlan = %[4]d +} +`, cid, rName, bgpAsn, vlan) } +func testAccDxTransitVirtualInterfaceConfig_tags(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` resource "aws_dx_transit_virtual_interface" "test" { - connection_id = %[1]q - + address_family = "ipv4" + bgp_asn = %[3]d dx_gateway_id = "${aws_dx_gateway.test.id}" + connection_id = %[1]q name = %[2]q - vlan = %[5]d + vlan = %[4]d + + tags = { + Name = %[2]q + Key1 = "Value1" + Key2 = "Value2a" + } +} +`, cid, rName, bgpAsn, vlan) +} + +func testAccDxTransitVirtualInterfaceConfig_tagsUpdated(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxTransitVirtualInterfaceConfig_base(rName, amzAsn) + fmt.Sprintf(` +resource "aws_dx_transit_virtual_interface" "test" { address_family = "ipv4" - bgp_asn = %[4]d - mtu = 8500 + bgp_asn = %[3]d + dx_gateway_id = "${aws_dx_gateway.test.id}" + connection_id = %[1]q + name = %[2]q + vlan = %[4]d tags = { - Environment = "test" + Name = %[2]q + Key2 = "Value2b" + Key3 = "Value3" } } -`, cid, rName, amzAsn, bgpAsn, vlan) +`, cid, rName, bgpAsn, vlan) } From 5a039e142881f781786ad4ccb62744f657bce474 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 31 Jul 2019 13:08:46 -0400 Subject: [PATCH 09/10] Set 'arn' in Read method. Remove duplicate code. --- ...source_aws_dx_transit_virtual_interface.go | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/aws/resource_aws_dx_transit_virtual_interface.go b/aws/resource_aws_dx_transit_virtual_interface.go index 017c6a3e678e..d527a459c44b 100644 --- a/aws/resource_aws_dx_transit_virtual_interface.go +++ b/aws/resource_aws_dx_transit_virtual_interface.go @@ -139,14 +139,6 @@ func resourceAwsDxTransitVirtualInterfaceCreate(d *schema.ResourceData, meta int } d.SetId(aws.StringValue(resp.VirtualInterface.VirtualInterfaceId)) - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) if err := dxTransitVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err @@ -170,6 +162,14 @@ func resourceAwsDxTransitVirtualInterfaceRead(d *schema.ResourceData, meta inter d.Set("address_family", vif.AddressFamily) d.Set("amazon_address", vif.AmazonAddress) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) d.Set("aws_device", vif.AwsDeviceV2) d.Set("bgp_asn", vif.Asn) d.Set("bgp_auth_key", vif.AuthKey) @@ -218,15 +218,6 @@ func resourceAwsDxTransitVirtualInterfaceImport(d *schema.ResourceData, meta int return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) } - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) - return []*schema.ResourceData{d}, nil } From 9763e96a893118cb07499aaf08f06998f6f89127 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 2 Aug 2019 17:05:32 -0400 Subject: [PATCH 10/10] Use common 'Exists' and 'Destroy' methods - https://github.com/terraform-providers/terraform-provider-aws/pull/9572. --- aws/dx_vif_test.go | 72 +++++++++++++++++++ ...e_aws_dx_transit_virtual_interface_test.go | 67 ++--------------- 2 files changed, 76 insertions(+), 63 deletions(-) create mode 100644 aws/dx_vif_test.go diff --git a/aws/dx_vif_test.go b/aws/dx_vif_test.go new file mode 100644 index 000000000000..a6b19cd75711 --- /dev/null +++ b/aws/dx_vif_test.go @@ -0,0 +1,72 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func testAccCheckDxVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).dxconn + + 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") + } + + resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + + for _, v := range resp.VirtualInterfaces { + if aws.StringValue(v.VirtualInterfaceId) == rs.Primary.ID { + *vif = *v + + return nil + } + } + + return fmt.Errorf("Direct Connect virtual interface (%s) not found", rs.Primary.ID) + } +} + +func testAccCheckDxVirtualInterfaceDestroy(s *terraform.State, t string) error { + conn := testAccProvider.Meta().(*AWSClient).dxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != t { + continue + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ + VirtualInterfaceId: aws.String(rs.Primary.ID), + }) + if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { + continue + } + if err != nil { + return err + } + + for _, v := range resp.VirtualInterfaces { + if aws.StringValue(v.VirtualInterfaceId) == rs.Primary.ID && aws.StringValue(v.VirtualInterfaceState) != directconnect.VirtualInterfaceStateDeleted { + return fmt.Errorf("[DESTROY ERROR] Direct Connect virtual interface (%s) not deleted", rs.Primary.ID) + } + } + } + + return nil +} diff --git a/aws/resource_aws_dx_transit_virtual_interface_test.go b/aws/resource_aws_dx_transit_virtual_interface_test.go index bc929bdff26b..4d653d700567 100644 --- a/aws/resource_aws_dx_transit_virtual_interface_test.go +++ b/aws/resource_aws_dx_transit_virtual_interface_test.go @@ -174,71 +174,12 @@ func testAccAwsDxTransitVirtualInterface_Tags(t *testing.T) { }) } -func testAccCheckAwsDxTransitVirtualInterfaceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_dx_transit_virtual_interface" { - continue - } - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - }) - if isAWSErr(err, directconnect.ErrCodeClientException, "does not exist") { - continue - } - if err != nil { - return err - } - - n := len(resp.VirtualInterfaces) - switch n { - case 0: - continue - case 1: - if aws.StringValue(resp.VirtualInterfaces[0].VirtualInterfaceState) == directconnect.VirtualInterfaceStateDeleted { - continue - } - return fmt.Errorf("still exist.") - default: - return fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, rs.Primary.ID) - } - } - - return nil -} - func testAccCheckAwsDxTransitVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - 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") - } - - resp, err := conn.DescribeVirtualInterfaces(&directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - }) - if err != nil { - return err - } - - if n := len(resp.VirtualInterfaces); n != 1 { - return fmt.Errorf("Found %d Direct Connect virtual interfaces for %s, expected 1", n, rs.Primary.ID) - } - - *vif = *resp.VirtualInterfaces[0] + return testAccCheckDxVirtualInterfaceExists(name, vif) +} - return nil - } +func testAccCheckAwsDxTransitVirtualInterfaceDestroy(s *terraform.State) error { + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_transit_virtual_interface") } func testAccDxTransitVirtualInterfaceConfig_base(rName string, amzAsn int) string {