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

Correctly refresh VGW attachment state #8576

Merged
merged 1 commit into from
May 13, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove as duplicate of vpnGatewayAttachmentStateRefresh().

// 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