Skip to content

Commit

Permalink
Merge pull request #12409 from Maarc-D/feat/add-data-ec2-transit-gate…
Browse files Browse the repository at this point in the history
…way-vpc-attatchments

add data ec2 transit gateway vpc attatchments
  • Loading branch information
ewbankkit authored Feb 24, 2022
2 parents adc1d6a + 75a18ca commit 3839bc6
Show file tree
Hide file tree
Showing 21 changed files with 1,149 additions and 882 deletions.
3 changes: 3 additions & 0 deletions .changelog/12409.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-data-source
aws_ec2_transit_gateway_vpc_attachments
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ func Provider() *schema.Provider {
"aws_ec2_transit_gateway_route_table": ec2.DataSourceTransitGatewayRouteTable(),
"aws_ec2_transit_gateway_route_tables": ec2.DataSourceTransitGatewayRouteTables(),
"aws_ec2_transit_gateway_vpc_attachment": ec2.DataSourceTransitGatewayVPCAttachment(),
"aws_ec2_transit_gateway_vpc_attachments": ec2.DataSourceTransitGatewayVPCAttachments(),
"aws_ec2_transit_gateway_vpn_attachment": ec2.DataSourceTransitGatewayVPNAttachment(),
"aws_eip": ec2.DataSourceEIP(),
"aws_eips": ec2.DataSourceEIPs(),
Expand Down
157 changes: 151 additions & 6 deletions internal/service/ec2/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -1815,23 +1815,86 @@ func FindVPCEndpointSubnetAssociationExists(conn *ec2.EC2, vpcEndpointID string,
}
}

// FindVPCPeeringConnectionByID returns the VPC peering connection corresponding to the specified identifier.
// Returns nil and potentially an error if no VPC peering connection is found.
func FindVPCPeeringConnection(conn *ec2.EC2, input *ec2.DescribeVpcPeeringConnectionsInput) (*ec2.VpcPeeringConnection, error) {
output, err := FindVPCPeeringConnections(conn, input)

if err != nil {
return nil, err
}

if len(output) == 0 || output[0] == nil || output[0].Status == nil {
return nil, tfresource.NewEmptyResultError(input)
}

if count := len(output); count > 1 {
return nil, tfresource.NewTooManyResultsError(count, input)
}

return output[0], nil
}

func FindVPCPeeringConnections(conn *ec2.EC2, input *ec2.DescribeVpcPeeringConnectionsInput) ([]*ec2.VpcPeeringConnection, error) {
var output []*ec2.VpcPeeringConnection

err := conn.DescribeVpcPeeringConnectionsPages(input, func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.VpcPeeringConnections {
if v != nil {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, ErrCodeInvalidVpcPeeringConnectionIDNotFound) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return output, nil
}

func FindVPCPeeringConnectionByID(conn *ec2.EC2, id string) (*ec2.VpcPeeringConnection, error) {
input := &ec2.DescribeVpcPeeringConnectionsInput{
VpcPeeringConnectionIds: aws.StringSlice([]string{id}),
}

output, err := conn.DescribeVpcPeeringConnections(input)
output, err := FindVPCPeeringConnection(conn, input)

if err != nil {
return nil, err
}

if output == nil || len(output.VpcPeeringConnections) == 0 {
return nil, nil
// See https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-basics.html#vpc-peering-lifecycle.
switch statusCode := aws.StringValue(output.Status.Code); statusCode {
case ec2.VpcPeeringConnectionStateReasonCodeDeleted,
ec2.VpcPeeringConnectionStateReasonCodeExpired,
ec2.VpcPeeringConnectionStateReasonCodeFailed,
ec2.VpcPeeringConnectionStateReasonCodeRejected:
return nil, &resource.NotFoundError{
Message: statusCode,
LastRequest: input,
}
}

// Eventual consistency check.
if aws.StringValue(output.VpcPeeringConnectionId) != id {
return nil, &resource.NotFoundError{
LastRequest: input,
}
}

return output.VpcPeeringConnections[0], nil
return output, nil
}

// FindVPNGatewayRoutePropagationExists returns NotFoundError if no route propagation for the specified VPN gateway is found.
Expand Down Expand Up @@ -2615,6 +2678,88 @@ func FindTransitGatewayRouteTablePropagation(conn *ec2.EC2, transitGatewayRouteT
return result, nil
}

func FindTransitGatewayVPCAttachment(conn *ec2.EC2, input *ec2.DescribeTransitGatewayVpcAttachmentsInput) (*ec2.TransitGatewayVpcAttachment, error) {
output, err := FindTransitGatewayVPCAttachments(conn, input)

if err != nil {
return nil, err
}

if len(output) == 0 || output[0] == nil {
return nil, tfresource.NewEmptyResultError(input)
}

if count := len(output); count > 1 {
return nil, tfresource.NewTooManyResultsError(count, input)
}

return output[0], nil
}

func FindTransitGatewayVPCAttachments(conn *ec2.EC2, input *ec2.DescribeTransitGatewayVpcAttachmentsInput) ([]*ec2.TransitGatewayVpcAttachment, error) {
var output []*ec2.TransitGatewayVpcAttachment

err := conn.DescribeTransitGatewayVpcAttachmentsPages(input, func(page *ec2.DescribeTransitGatewayVpcAttachmentsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.TransitGatewayVpcAttachments {
if v != nil {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, ErrCodeInvalidTransitGatewayAttachmentIDNotFound) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return output, nil
}

func FindTransitGatewayVPCAttachmentByID(conn *ec2.EC2, id string) (*ec2.TransitGatewayVpcAttachment, error) {
input := &ec2.DescribeTransitGatewayVpcAttachmentsInput{
TransitGatewayAttachmentIds: aws.StringSlice([]string{id}),
}

output, err := FindTransitGatewayVPCAttachment(conn, input)

if err != nil {
return nil, err
}

// See https://docs.aws.amazon.com/vpc/latest/tgw/tgw-vpc-attachments.html#vpc-attachment-lifecycle.
switch state := aws.StringValue(output.State); state {
case ec2.TransitGatewayAttachmentStateDeleted,
ec2.TransitGatewayAttachmentStateFailed,
ec2.TransitGatewayAttachmentStateRejected:
return nil, &resource.NotFoundError{
Message: state,
LastRequest: input,
}

}

// Eventual consistency check.
if aws.StringValue(output.TransitGatewayAttachmentId) != id {
return nil, &resource.NotFoundError{
LastRequest: input,
}
}

return output, nil
}

func FindDHCPOptions(conn *ec2.EC2, input *ec2.DescribeDhcpOptionsInput) (*ec2.DhcpOptions, error) {
output, err := FindDHCPOptionses(conn, input)

Expand Down
39 changes: 0 additions & 39 deletions internal/service/ec2/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,42 +180,3 @@ func FlattenSecurityGroups(list []*ec2.UserIdGroupPair, ownerId *string) []*Grou
}
return result
}

func expandVPCPeeringConnectionOptions(vOptions []interface{}, crossRegionPeering bool) *ec2.PeeringConnectionOptionsRequest {
if len(vOptions) == 0 || vOptions[0] == nil {
return nil
}

mOptions := vOptions[0].(map[string]interface{})

options := &ec2.PeeringConnectionOptionsRequest{}

if v, ok := mOptions["allow_remote_vpc_dns_resolution"].(bool); ok {
options.AllowDnsResolutionFromRemoteVpc = aws.Bool(v)
}
if !crossRegionPeering {
if v, ok := mOptions["allow_classic_link_to_remote_vpc"].(bool); ok {
options.AllowEgressFromLocalClassicLinkToRemoteVpc = aws.Bool(v)
}
if v, ok := mOptions["allow_vpc_to_remote_classic_link"].(bool); ok {
options.AllowEgressFromLocalVpcToRemoteClassicLink = aws.Bool(v)
}
}

return options
}

func flattenVPCPeeringConnectionOptions(options *ec2.VpcPeeringConnectionOptionsDescription) []interface{} {
// When the VPC Peering Connection is pending acceptance,
// the details about accepter and/or requester peering
// options would not be included in the response.
if options == nil {
return []interface{}{}
}

return []interface{}{map[string]interface{}{
"allow_remote_vpc_dns_resolution": aws.BoolValue(options.AllowDnsResolutionFromRemoteVpc),
"allow_classic_link_to_remote_vpc": aws.BoolValue(options.AllowEgressFromLocalClassicLinkToRemoteVpc),
"allow_vpc_to_remote_classic_link": aws.BoolValue(options.AllowEgressFromLocalVpcToRemoteClassicLink),
}}
}
49 changes: 23 additions & 26 deletions internal/service/ec2/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ec2

import (
"fmt"
"log"
"strconv"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -575,40 +574,38 @@ func StatusVPCIPv6CIDRBlockAssociationState(conn *ec2.EC2, id string) resource.S
}
}

const (
vpcPeeringConnectionStatusNotFound = "NotFound"
vpcPeeringConnectionStatusUnknown = "Unknown"
)

// StatusVPCPeeringConnection fetches the VPC peering connection and its status
func StatusVPCPeeringConnection(conn *ec2.EC2, vpcPeeringConnectionID string) resource.StateRefreshFunc {
func StatusVPCPeeringConnectionActive(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
vpcPeeringConnection, err := FindVPCPeeringConnectionByID(conn, vpcPeeringConnectionID)
if tfawserr.ErrCodeEquals(err, ErrCodeInvalidVpcPeeringConnectionIDNotFound) {
return nil, vpcPeeringConnectionStatusNotFound, nil
// Don't call FindVPCPeeringConnectionByID as it maps useful status codes to NotFoundError.
output, err := FindVPCPeeringConnection(conn, &ec2.DescribeVpcPeeringConnectionsInput{
VpcPeeringConnectionIds: aws.StringSlice([]string{id}),
})

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, vpcPeeringConnectionStatusUnknown, err
return nil, "", err
}

// Sometimes AWS just has consistency issues and doesn't see
// our peering connection yet. Return an empty state.
if vpcPeeringConnection == nil || vpcPeeringConnection.Status == nil {
return nil, vpcPeeringConnectionStatusNotFound, nil
}
return output, aws.StringValue(output.Status.Code), nil
}
}

statusCode := aws.StringValue(vpcPeeringConnection.Status.Code)
func StatusVPCPeeringConnectionDeleted(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := FindVPCPeeringConnectionByID(conn, id)

if tfresource.NotFound(err) {
return nil, "", nil
}

// https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-basics.html#vpc-peering-lifecycle
switch statusCode {
case ec2.VpcPeeringConnectionStateReasonCodeFailed:
log.Printf("[WARN] VPC Peering Connection (%s): %s: %s", vpcPeeringConnectionID, statusCode, aws.StringValue(vpcPeeringConnection.Status.Message))
fallthrough
case ec2.VpcPeeringConnectionStateReasonCodeDeleted, ec2.VpcPeeringConnectionStateReasonCodeExpired, ec2.VpcPeeringConnectionStateReasonCodeRejected:
return nil, vpcPeeringConnectionStatusNotFound, nil
if err != nil {
return nil, "", err
}

return vpcPeeringConnection, statusCode, nil
return output, aws.StringValue(output.Status.Code), nil
}
}

Expand Down
49 changes: 14 additions & 35 deletions internal/service/ec2/sweep.go
Original file line number Diff line number Diff line change
Expand Up @@ -1962,48 +1962,21 @@ func sweepVPCPeeringConnections(region string) error {
if err != nil {
return fmt.Errorf("error getting client: %s", err)
}

conn := client.(*conns.AWSClient).EC2Conn
input := &ec2.DescribeVpcPeeringConnectionsInput{}
conn := client.(*conns.AWSClient).EC2Conn
sweepResources := make([]*sweep.SweepResource, 0)

err = conn.DescribeVpcPeeringConnectionsPages(input, func(page *ec2.DescribeVpcPeeringConnectionsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, vpcPeeringConnection := range page.VpcPeeringConnections {
deletedStatuses := map[string]bool{
ec2.VpcPeeringConnectionStateReasonCodeDeleted: true,
ec2.VpcPeeringConnectionStateReasonCodeExpired: true,
ec2.VpcPeeringConnectionStateReasonCodeFailed: true,
ec2.VpcPeeringConnectionStateReasonCodeRejected: true,
}

if _, ok := deletedStatuses[aws.StringValue(vpcPeeringConnection.Status.Code)]; ok {
continue
}

id := aws.StringValue(vpcPeeringConnection.VpcPeeringConnectionId)
input := &ec2.DeleteVpcPeeringConnectionInput{
VpcPeeringConnectionId: vpcPeeringConnection.VpcPeeringConnectionId,
}

log.Printf("[INFO] Deleting EC2 VPC Peering Connection: %s", id)

_, err := conn.DeleteVpcPeeringConnection(input)

if tfawserr.ErrMessageContains(err, "InvalidVpcPeeringConnectionID.NotFound", "") {
continue
}

if err != nil {
log.Printf("[ERROR] Error deleting EC2 VPC Peering Connection (%s): %s", id, err)
continue
}
for _, v := range page.VpcPeeringConnections {
r := ResourceVPCPeeringConnection()
d := r.Data(nil)
d.SetId(aws.StringValue(v.VpcPeeringConnectionId))

if err := WaitForVPCPeeringConnectionDeletion(conn, id, 5*time.Minute); err != nil { //nolint:gomnd
log.Printf("[ERROR] Error waiting for EC2 VPC Peering Connection (%s) to be deleted: %s", id, err)
}
sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client))
}

return !lastPage
Expand All @@ -2015,7 +1988,13 @@ func sweepVPCPeeringConnections(region string) error {
}

if err != nil {
return fmt.Errorf("Error describing EC2 VPC Peering Connections: %s", err)
return fmt.Errorf("error listing EC2 VPC Peering Connections (%s): %w", region, err)
}

err = sweep.SweepOrchestrator(sweepResources)

if err != nil {
return fmt.Errorf("error sweeping EC2 VPC Peering Connections (%s): %w", region, err)
}

return nil
Expand Down
3 changes: 3 additions & 0 deletions internal/service/ec2/transit_gateway_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ func TestAccEC2TransitGatewayDataSource_serial(t *testing.T) {
"Filter": testAccTransitGatewayVPCAttachmentDataSource_Filter,
"ID": testAccTransitGatewayVPCAttachmentDataSource_ID,
},
"VpcAttachments": {
"Filter": testAccTransitGatewayVPCAttachmentsDataSource_Filter,
},
"VpnAttachment": {
"Filter": testAccTransitGatewayVPNAttachmentDataSource_filter,
"TransitGatewayIdAndVpnConnectionId": testAccTransitGatewayVPNAttachmentDataSource_TransitGatewayIdAndVpnConnectionID,
Expand Down
Loading

0 comments on commit 3839bc6

Please sign in to comment.