Skip to content

Commit

Permalink
Merge pull request #8576 from ewbankkit/issue-8575
Browse files Browse the repository at this point in the history
Correctly refresh VGW attachment state
  • Loading branch information
bflad authored May 13, 2019
2 parents d210c0c + f706a08 commit bc9c1d5
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 66 deletions.
71 changes: 18 additions & 53 deletions aws/resource_aws_vpn_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func resourceAwsVpnGatewayRead(d *schema.ResourceData, meta interface{}) error {
}

vpnAttachment := vpnGatewayGetAttachment(vpnGateway)
if len(vpnGateway.VpcAttachments) == 0 || *vpnAttachment.State == "detached" {
if vpnAttachment == nil {
// Gateway exists but not attached to the VPC
d.Set("vpc_id", "")
} else {
Expand Down Expand Up @@ -181,7 +181,9 @@ func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error
func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

if d.Get("vpc_id").(string) == "" {
vpcId := d.Get("vpc_id").(string)

if vpcId == "" {
log.Printf(
"[DEBUG] Not attaching VPN Gateway '%s' as no VPC ID is set",
d.Id())
Expand All @@ -191,11 +193,11 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error
log.Printf(
"[INFO] Attaching VPN Gateway '%s' to VPC '%s'",
d.Id(),
d.Get("vpc_id").(string))
vpcId)

req := &ec2.AttachVpnGatewayInput{
VpnGatewayId: aws.String(d.Id()),
VpcId: aws.String(d.Get("vpc_id").(string)),
VpcId: aws.String(vpcId),
}

err := resource.Retry(1*time.Minute, func() *resource.RetryError {
Expand All @@ -218,7 +220,7 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error
stateConf := &resource.StateChangeConf{
Pending: []string{"detached", "attaching"},
Target: []string{"attached"},
Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id()),
Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, d.Id()),
Timeout: 15 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
Expand All @@ -234,9 +236,10 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).ec2conn

// Get the old VPC ID to detach from
vpcID, _ := d.GetChange("vpc_id")
vpcIdRaw, _ := d.GetChange("vpc_id")
vpcId := vpcIdRaw.(string)

if vpcID.(string) == "" {
if vpcId == "" {
log.Printf(
"[DEBUG] Not detaching VPN Gateway '%s' as no VPC ID is set",
d.Id())
Expand All @@ -246,12 +249,12 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error
log.Printf(
"[INFO] Detaching VPN Gateway '%s' from VPC '%s'",
d.Id(),
vpcID.(string))
vpcId)

wait := true
_, err := conn.DetachVpnGateway(&ec2.DetachVpnGatewayInput{
VpnGatewayId: aws.String(d.Id()),
VpcId: aws.String(vpcID.(string)),
VpcId: aws.String(vpcId),
})
if err != nil {
ec2err, ok := err.(awserr.Error)
Expand Down Expand Up @@ -279,7 +282,7 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error
stateConf := &resource.StateChangeConf{
Pending: []string{"attached", "detaching", "available"},
Target: []string{"detached"},
Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id()),
Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, d.Id()),
Timeout: 10 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
Expand All @@ -291,50 +294,12 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error
return nil
}

// vpnGatewayAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// the state of a VPN gateway's attachment
func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
var start time.Time
return func() (interface{}, string, error) {
if start.IsZero() {
start = time.Now()
}

resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{
VpnGatewayIds: []*string{aws.String(id)},
})

if err != nil {
if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" {
resp = nil
} else {
log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err)
return nil, "", err
}
}

if resp == nil {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil, "", nil
}

vpnGateway := resp.VpnGateways[0]
if len(vpnGateway.VpcAttachments) == 0 {
// No attachments, we're detached
return vpnGateway, "detached", nil
}

vpnAttachment := vpnGatewayGetAttachment(vpnGateway)
return vpnGateway, *vpnAttachment.State, nil
}
}

// vpnGatewayGetAttachment returns any VGW attachment that's in "attached" state or nil.
func vpnGatewayGetAttachment(vgw *ec2.VpnGateway) *ec2.VpcAttachment {
for _, v := range vgw.VpcAttachments {
if *v.State == "attached" {
return v
for _, vpcAttachment := range vgw.VpcAttachments {
if aws.StringValue(vpcAttachment.State) == ec2.AttachmentStatusAttached {
return vpcAttachment
}
}
return &ec2.VpcAttachment{State: aws.String("detached")}
return nil
}
27 changes: 14 additions & 13 deletions aws/resource_aws_vpn_gateway_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func resourceAwsVpnGatewayAttachmentRead(d *schema.ResourceData, meta interface{
}

vga := vpnGatewayGetAttachment(vgw)
if len(vgw.VpcAttachments) == 0 || *vga.State == "detached" {
if vga == nil {
d.Set("vpc_id", "")
return nil
}
Expand Down Expand Up @@ -163,12 +163,9 @@ func resourceAwsVpnGatewayAttachmentDelete(d *schema.ResourceData, meta interfac
func vpnGatewayAttachmentStateRefresh(conn *ec2.EC2, vpcId, vgwId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{
Filters: []*ec2.Filter{
{
Name: aws.String("attachment.vpc-id"),
Values: []*string{aws.String(vpcId)},
},
},
Filters: buildEC2AttributeFilterList(map[string]string{
"attachment.vpc-id": vpcId,
}),
VpnGatewayIds: []*string{aws.String(vgwId)},
})

Expand All @@ -187,15 +184,19 @@ func vpnGatewayAttachmentStateRefresh(conn *ec2.EC2, vpcId, vgwId string) resour
}

vgw := resp.VpnGateways[0]
if len(vgw.VpcAttachments) == 0 {
return vgw, "detached", nil
}

vga := vpnGatewayGetAttachment(vgw)
return vgw, vpnGatewayGetAttachmentState(vgw, vpcId), nil
}
}

log.Printf("[DEBUG] VPN Gateway %q attachment status: %s", vgwId, *vga.State)
return vgw, *vga.State, nil
// vpnGatewayGetAttachmentState returns the state of any VGW attachment to the specified VPC or "detached".
func vpnGatewayGetAttachmentState(vgw *ec2.VpnGateway, vpcId string) string {
for _, vpcAttachment := range vgw.VpcAttachments {
if aws.StringValue(vpcAttachment.VpcId) == vpcId {
return aws.StringValue(vpcAttachment.State)
}
}
return ec2.AttachmentStatusDetached
}

func vpnGatewayAttachmentId(vpcId, vgwId string) string {
Expand Down

0 comments on commit bc9c1d5

Please sign in to comment.