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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/vpn_connection - add arn attribute + tags test #13836

Merged
merged 3 commits into from Jun 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 29 additions & 15 deletions aws/resource_aws_vpn_connection.go
Expand Up @@ -10,7 +10,7 @@ import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
Expand Down Expand Up @@ -70,6 +70,10 @@ func resourceAwsVpnConnection() *schema.Resource {
},

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"vpn_gateway_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -338,7 +342,7 @@ func vpnConnectionRefreshFunc(conn *ec2.EC2, connectionId string) resource.State
})

if err != nil {
if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnConnectionID.NotFound" {
if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") {
resp = nil
} else {
log.Printf("Error on VPNConnectionRefresh: %s", err)
Expand All @@ -351,7 +355,7 @@ func vpnConnectionRefreshFunc(conn *ec2.EC2, connectionId string) resource.State
}

connection := resp.VpnConnections[0]
return connection, *connection.State, nil
return connection, aws.StringValue(connection.State), nil
}
}

Expand Down Expand Up @@ -475,6 +479,16 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro
return err
}

arn := arn.ARN{
Partition: meta.(*AWSClient).partition,
Service: "ec2",
Region: meta.(*AWSClient).region,
AccountID: meta.(*AWSClient).accountid,
Resource: fmt.Sprintf("vpn-connection/%s", d.Id()),
}.String()

d.Set("arn", arn)

return nil
}

Expand Down Expand Up @@ -519,11 +533,11 @@ func routesToMapList(routes []*ec2.VpnStaticRoute) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(routes))
for _, r := range routes {
staticRoute := make(map[string]interface{})
staticRoute["destination_cidr_block"] = *r.DestinationCidrBlock
staticRoute["state"] = *r.State
staticRoute["destination_cidr_block"] = aws.StringValue(r.DestinationCidrBlock)
staticRoute["state"] = aws.StringValue(r.State)

if r.Source != nil {
staticRoute["source"] = *r.Source
staticRoute["source"] = aws.StringValue(r.Source)
}

result = append(result, staticRoute)
Expand All @@ -537,14 +551,14 @@ func telemetryToMapList(telemetry []*ec2.VgwTelemetry) []map[string]interface{}
result := make([]map[string]interface{}, 0, len(telemetry))
for _, t := range telemetry {
vgw := make(map[string]interface{})
vgw["accepted_route_count"] = *t.AcceptedRouteCount
vgw["outside_ip_address"] = *t.OutsideIpAddress
vgw["status"] = *t.Status
vgw["status_message"] = *t.StatusMessage
vgw["accepted_route_count"] = aws.Int64Value(t.AcceptedRouteCount)
vgw["outside_ip_address"] = aws.StringValue(t.OutsideIpAddress)
vgw["status"] = aws.StringValue(t.Status)
vgw["status_message"] = aws.StringValue(t.StatusMessage)

// LastStatusChange is a time.Time(). Convert it into a string
// so it can be handled by schema's type system.
vgw["last_status_change"] = t.LastStatusChange.String()
vgw["last_status_change"] = t.LastStatusChange.Format(time.RFC3339)
result = append(result, vgw)
}

Expand All @@ -557,8 +571,8 @@ func waitForEc2VpnConnectionAvailable(conn *ec2.EC2, id string) error {
// slow at coming up or going down. There's also no point in checking
// more frequently than every ten seconds.
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: []string{"available"},
Pending: []string{ec2.VpnStatePending},
Target: []string{ec2.VpnStateAvailable},
Refresh: vpnConnectionRefreshFunc(conn, id),
Timeout: 40 * time.Minute,
Delay: 10 * time.Second,
Expand All @@ -578,8 +592,8 @@ func waitForEc2VpnConnectionDeletion(conn *ec2.EC2, id string) error {
// wait to ensure any other modifications the user might make to their
// VPC stack can safely run.
stateConf := &resource.StateChangeConf{
Pending: []string{"deleting"},
Target: []string{"deleted"},
Pending: []string{ec2.VpnStateDeleting},
Target: []string{ec2.VpnStateDeleted},
Refresh: vpnConnectionRefreshFunc(conn, id),
Timeout: 30 * time.Minute,
Delay: 10 * time.Second,
Expand Down
152 changes: 111 additions & 41 deletions aws/resource_aws_vpn_connection_test.go
Expand Up @@ -5,12 +5,10 @@ import (
"log"
"regexp"
"testing"
"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/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
Expand Down Expand Up @@ -90,6 +88,8 @@ func TestAccAWSVpnConnection_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnectionExists(resourceName, &vpn),
resource.TestCheckResourceAttr(resourceName, "transit_gateway_attachment_id", ""),
testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpn-connection/vpn-.+`)),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
),
},
{
Expand Down Expand Up @@ -258,6 +258,51 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) {
})
}

func TestAccAWSVpnConnection_tags(t *testing.T) {
rBgpAsn := acctest.RandIntRange(64512, 65534)
resourceName := "aws_vpn_connection.test"
var vpn ec2.VpnConnection

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccAwsVpnConnectionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAwsVpnConnectionConfigTags1(rBgpAsn, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnectionExists(resourceName, &vpn),
resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccAwsVpnConnectionConfigTags2(rBgpAsn, "key1", "value1updated", "key2", "value2"),
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnectionExists(resourceName, &vpn),
resource.TestCheckResourceAttr(resourceName, "tags.%", "2"),
resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"),
resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
),
},
{
Config: testAccAwsVpnConnectionConfigTags1(rBgpAsn, "key2", "value2"),
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnectionExists(resourceName, &vpn),
resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
),
},
},
})
}

func TestAccAWSVpnConnection_disappears(t *testing.T) {
rBgpAsn := acctest.RandIntRange(64512, 65534)
resourceName := "aws_vpn_connection.test"
Expand All @@ -272,52 +317,14 @@ func TestAccAWSVpnConnection_disappears(t *testing.T) {
Config: testAccAwsVpnConnectionConfig(rBgpAsn),
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnectionExists(resourceName, &vpn),
testAccAWSVpnConnectionDisappears(&vpn),
testAccCheckResourceDisappears(testAccProvider, resourceAwsVpnConnection(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccAWSVpnConnectionDisappears(connection *ec2.VpnConnection) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn

_, err := conn.DeleteVpnConnection(&ec2.DeleteVpnConnectionInput{
VpnConnectionId: connection.VpnConnectionId,
})

if err != nil {
return err
}

return resource.Retry(40*time.Minute, func() *resource.RetryError {
opts := &ec2.DescribeVpnConnectionsInput{
VpnConnectionIds: []*string{connection.VpnConnectionId},
}
resp, err := conn.DescribeVpnConnections(opts)
if err != nil {
cgw, ok := err.(awserr.Error)
if ok && cgw.Code() == "InvalidVpnConnectionID.NotFound" {
return nil
}
if ok && cgw.Code() == "IncorrectState" {
return resource.RetryableError(fmt.Errorf(
"Waiting for VPN Connection to be in the correct state: %v", connection.VpnConnectionId))
}
return resource.NonRetryableError(
fmt.Errorf("Error retrieving VPN Connection: %s", err))
}
if *resp.VpnConnections[0].State == "deleted" {
return nil
}
return resource.RetryableError(fmt.Errorf(
"Waiting for VPN Connection: %v", connection.VpnConnectionId))
})
}
}

func testAccAwsVpnConnectionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources {
Expand Down Expand Up @@ -576,6 +583,69 @@ resource "aws_vpn_connection" "test" {
`, rBgpAsn, tunnelCidr, psk, tunnelCidr2, psk2)
}

func testAccAwsVpnConnectionConfigTags1(rBgpAsn int, tagKey1, tagValue1 string) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags = {
Name = "vpn_gateway"
}
}

resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"

tags = {
Name = "main-customer-gateway"
}
}

resource "aws_vpn_connection" "test" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = true

tags = {
%[2]q = %[3]q
}
}
`, rBgpAsn, tagKey1, tagValue1)
}

func testAccAwsVpnConnectionConfigTags2(rBgpAsn int, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags = {
Name = "vpn_gateway"
}
}

resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"

tags = {
Name = "main-customer-gateway"
}
}

resource "aws_vpn_connection" "test" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = true

tags = {
%[2]q = %[3]q
%[4]q = %[5]q
}
}
`, rBgpAsn, tagKey1, tagValue1, tagKey2, tagValue2)
}

// Test our VPN tunnel config XML parsing
const testAccAwsVpnTunnelInfoXML = `
<vpn_connection id="vpn-abc123">
Expand Down
1 change: 1 addition & 0 deletions website/docs/index.html.markdown
Expand Up @@ -309,6 +309,7 @@ for more information about connecting to alternate AWS endpoints or AWS compatib
- [`aws_vpc_endpoint` resource](/docs/providers/aws/r/vpc_endpoint.html)
- [`aws_vpc_endpoint_service` data source](/docs/providers/aws/d/vpc_endpoint_service.html)
- [`aws_vpc_endpoint_service` resource](/docs/providers/aws/r/vpc_endpoint_service.html)
- [`aws_vpn_connection` resource](/docs/providers/aws/r/vpn_connection.html)
- [`aws_vpn_gateway` data source](/docs/providers/aws/d/vpn_gateway.html)
- [`aws_vpn_gateway` resource](/docs/providers/aws/r/vpn_gateway.html)
- [`aws_waf_geo_match_set` resource](/docs/providers/aws/r/waf_geo_match_set.html)
Expand Down
3 changes: 1 addition & 2 deletions website/docs/r/vpn_connection.html.markdown
Expand Up @@ -88,11 +88,11 @@ Other arguments:

In addition to all arguments above, the following attributes are exported:

* `arn` - Amazon Resource Name (ARN) of the VPN Connection.
* `id` - The amazon-assigned ID of the VPN connection.
* `customer_gateway_configuration` - The configuration information for the VPN connection's customer gateway (in the native XML format).
* `customer_gateway_id` - The ID of the customer gateway to which the connection is attached.
* `static_routes_only` - Whether the VPN connection uses static routes exclusively.
* `tags` - Tags applied to the connection.
* `transit_gateway_attachment_id` - When associated with an EC2 Transit Gateway (`transit_gateway_id` argument), the attachment ID.
* `tunnel1_address` - The public IP address of the first VPN tunnel.
* `tunnel1_cgw_inside_address` - The RFC 6890 link-local address of the first VPN tunnel (Customer Gateway Side).
Expand All @@ -106,7 +106,6 @@ In addition to all arguments above, the following attributes are exported:
* `tunnel2_preshared_key` - The preshared key of the second VPN tunnel.
* `tunnel2_bgp_asn` - The bgp asn number of the second VPN tunnel.
* `tunnel2_bgp_holdtime` - The bgp holdtime of the second VPN tunnel.
* `type` - The type of VPN connection.
* `vpn_gateway_id` - The ID of the virtual private gateway to which the connection is attached.


Expand Down