Skip to content

Commit

Permalink
Fix aws_ram_resource_share_accepter eventual consistency problems
Browse files Browse the repository at this point in the history
  • Loading branch information
shuheiktgw authored and YakDriver committed Apr 27, 2021
1 parent 6034deb commit 559e521
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 122 deletions.
186 changes: 186 additions & 0 deletions aws/internal/service/ram/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package finder

import (
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ram"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

// ResourceShareOwnerOtherAccountsByArn returns the resource share owned by other accounts corresponding to the specified ARN.
// Returns nil if no configuration is found.
func ResourceShareOwnerOtherAccountsByArn(conn *ram.RAM, arn string) (*ram.ResourceShare, error) {
listResourceSharesInput := &ram.GetResourceSharesInput{
ResourceOwner: aws.String(ram.ResourceOwnerOtherAccounts),
ResourceShareArns: aws.StringSlice([]string{arn}),
}

return resourceShare(conn, listResourceSharesInput)
}

// ResourceShareOwnerSelfByArn returns the resource share owned by own account corresponding to the specified ARN.
// Returns nil if no configuration is found.
func ResourceShareOwnerSelfByArn(conn *ram.RAM, arn string) (*ram.ResourceShare, error) {
listResourceSharesInput := &ram.GetResourceSharesInput{
ResourceOwner: aws.String(ram.ResourceOwnerSelf),
ResourceShareArns: aws.StringSlice([]string{arn}),
}

return resourceShare(conn, listResourceSharesInput)
}

// ResourceShareInvitationByResourceShareArnAndStatus returns the resource share invitation corresponding to the specified resource share ARN.
// Returns nil if no configuration is found.
func ResourceShareInvitationByResourceShareArnAndStatus(conn *ram.RAM, resourceShareArn, status string) (*ram.ResourceShareInvitation, error) {
var invitation *ram.ResourceShareInvitation

// Retry for Ram resource share invitation eventual consistency
err := resource.Retry(30*time.Second, func() *resource.RetryError {
i, err := resourceShareInvitationByResourceShareArnAndStatus(conn, resourceShareArn, status)
invitation = i

if err != nil {
return resource.NonRetryableError(err)
}

if invitation == nil {
return resource.RetryableError(&resource.NotFoundError{})
}

return nil
})

if tfresource.TimedOut(err) {
invitation, err = resourceShareInvitationByResourceShareArnAndStatus(conn, resourceShareArn, status)
}

if invitation == nil {
return nil, nil
}

if err != nil {
return nil, err
}

return invitation, nil
}

// ResourceShareInvitationByArn returns the resource share invitation corresponding to the specified ARN.
// Returns nil if no configuration is found.
func ResourceShareInvitationByArn(conn *ram.RAM, arn string) (*ram.ResourceShareInvitation, error) {
var invitation *ram.ResourceShareInvitation

// Retry for Ram resource share invitation eventual consistency
err := resource.Retry(30*time.Second, func() *resource.RetryError {
i, err := resourceShareInvitationByArn(conn, arn)
invitation = i

if err != nil {
return resource.NonRetryableError(err)
}

if invitation == nil {
resource.RetryableError(&resource.NotFoundError{})
}

return nil
})

if tfresource.TimedOut(err) {
invitation, err = resourceShareInvitationByArn(conn, arn)
}

if invitation == nil {
return nil, nil
}

if err != nil {
return nil, err
}

return invitation, nil
}

func resourceShare(conn *ram.RAM, input *ram.GetResourceSharesInput) (*ram.ResourceShare, error) {
var shares *ram.GetResourceSharesOutput

// Retry for Ram resource share eventual consistency
err := resource.Retry(30*time.Second, func() *resource.RetryError {
ss, err := conn.GetResourceShares(input)
shares = ss

if tfawserr.ErrCodeEquals(err, ram.ErrCodeUnknownResourceException) {
return resource.RetryableError(err)
}

if err != nil {
return resource.NonRetryableError(err)
}

if len(shares.ResourceShares) == 0 {
return resource.RetryableError(&resource.NotFoundError{})
}

return nil
})

if tfresource.TimedOut(err) {
shares, err = conn.GetResourceShares(input)
}

if err != nil {
return nil, err
}

if shares == nil || len(shares.ResourceShares) == 0 {
return nil, nil
}

return shares.ResourceShares[0], nil
}

func resourceShareInvitationByResourceShareArnAndStatus(conn *ram.RAM, resourceShareArn, status string) (*ram.ResourceShareInvitation, error) {
var invitation *ram.ResourceShareInvitation

input := &ram.GetResourceShareInvitationsInput{
ResourceShareArns: []*string{aws.String(resourceShareArn)},
}

err := conn.GetResourceShareInvitationsPages(input, func(page *ram.GetResourceShareInvitationsOutput, lastPage bool) bool {
for _, rsi := range page.ResourceShareInvitations {
if aws.StringValue(rsi.Status) == status {
invitation = rsi
return false
}
}

return !lastPage
})

if err != nil {
return nil, err
}

return invitation, nil
}

func resourceShareInvitationByArn(conn *ram.RAM, arn string) (*ram.ResourceShareInvitation, error) {
input := &ram.GetResourceShareInvitationsInput{
ResourceShareInvitationArns: []*string{aws.String(arn)},
}

output, err := conn.GetResourceShareInvitations(input)

if err != nil {
return nil, err
}

if len(output.ResourceShareInvitations) == 0 {
return nil, nil
}

return output.ResourceShareInvitations[0], nil
}
55 changes: 55 additions & 0 deletions aws/internal/service/ram/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package waiter

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ram"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ram/finder"
)

const (
resourceShareInvitationStatusNotFound = "NotFound"
resourceShareInvitationStatusUnknown = "Unknown"

resourceShareStatusNotFound = "NotFound"
resourceShareStatusUnknown = "Unknown"
)

// ResourceShareInvitationStatus fetches the ResourceShareInvitation and its Status
func ResourceShareInvitationStatus(conn *ram.RAM, arn string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
invitation, err := finder.ResourceShareInvitationByArn(conn, arn)

if err != nil {
return nil, resourceShareInvitationStatusUnknown, err
}

if invitation == nil {
return nil, resourceShareInvitationStatusNotFound, nil
}

return invitation, aws.StringValue(invitation.Status), nil
}
}

// ResourceShareOwnerSelfStatus fetches the ResourceShare and its Status
func ResourceShareOwnerSelfStatus(conn *ram.RAM, arn string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
share, err := finder.ResourceShareOwnerSelfByArn(conn, arn)

if err != nil {
if tfawserr.ErrCodeEquals(err, ram.ErrCodeUnknownResourceException) {
return nil, resourceShareStatusNotFound, nil
}

return nil, resourceShareStatusUnknown, err
}

if share == nil {
return nil, resourceShareStatusNotFound, nil
}

return share, aws.StringValue(share.Status), nil
}
}
80 changes: 80 additions & 0 deletions aws/internal/service/ram/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package waiter

import (
"time"

"github.com/aws/aws-sdk-go/service/ram"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

// ResourceShareInvitationAccepted waits for a ResourceShareInvitation to return ACCEPTED
func ResourceShareInvitationAccepted(conn *ram.RAM, arn string, timeout time.Duration) (*ram.ResourceShareInvitation, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareInvitationStatusPending},
Target: []string{ram.ResourceShareInvitationStatusAccepted},
Refresh: ResourceShareInvitationStatus(conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*ram.ResourceShareInvitation); ok {
return v, err
}

return nil, err
}

// ResourceShareOwnedBySelfDisassociated waits for a ResourceShare owned by own account to be disassociated
func ResourceShareOwnedBySelfDisassociated(conn *ram.RAM, arn string, timeout time.Duration) (*ram.ResourceShare, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareAssociationStatusAssociated},
Target: []string{},
Refresh: ResourceShareOwnerSelfStatus(conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*ram.ResourceShare); ok {
return v, err
}

return nil, err
}

// ResourceShareOwnedBySelfActive waits for a ResourceShare owned by own account to return ACTIVE
func ResourceShareOwnedBySelfActive(conn *ram.RAM, arn string, timeout time.Duration) (*ram.ResourceShare, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusPending},
Target: []string{ram.ResourceShareStatusActive},
Refresh: ResourceShareOwnerSelfStatus(conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*ram.ResourceShare); ok {
return v, err
}

return nil, err
}

// ResourceShareOwnedBySelfDeleted waits for a ResourceShare owned by own account to return DELETED
func ResourceShareOwnedBySelfDeleted(conn *ram.RAM, arn string, timeout time.Duration) (*ram.ResourceShare, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusDeleting},
Target: []string{ram.ResourceShareStatusDeleted},
Refresh: ResourceShareOwnerSelfStatus(conn, arn),
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*ram.ResourceShare); ok {
return v, err
}

return nil, err
}
42 changes: 3 additions & 39 deletions aws/resource_aws_ram_resource_share.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ram"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ram/waiter"
)

func resourceAwsRamResourceShare() *schema.Resource {
Expand Down Expand Up @@ -75,14 +75,7 @@ func resourceAwsRamResourceShareCreate(d *schema.ResourceData, meta interface{})

d.SetId(aws.StringValue(createResp.ResourceShare.ResourceShareArn))

stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusPending},
Target: []string{ram.ResourceShareStatusActive},
Refresh: resourceAwsRamResourceShareStateRefreshFunc(conn, d.Id()),
Timeout: d.Timeout(schema.TimeoutCreate),
}

_, err = stateConf.WaitForState()
_, err = waiter.ResourceShareOwnedBySelfActive(conn, d.Id(), d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error waiting for RAM resource share (%s) to become ready: %s", d.Id(), err)
}
Expand Down Expand Up @@ -191,40 +184,11 @@ func resourceAwsRamResourceShareDelete(d *schema.ResourceData, meta interface{})
return fmt.Errorf("Error deleting RAM resource share %s: %s", d.Id(), err)
}

stateConf := &resource.StateChangeConf{
Pending: []string{ram.ResourceShareStatusDeleting},
Target: []string{ram.ResourceShareStatusDeleted},
Refresh: resourceAwsRamResourceShareStateRefreshFunc(conn, d.Id()),
Timeout: d.Timeout(schema.TimeoutDelete),
}
_, err = waiter.ResourceShareOwnedBySelfDeleted(conn, d.Id(), d.Timeout(schema.TimeoutDelete))

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for RAM resource share (%s) to become ready: %s", d.Id(), err)
}

return nil
}

func resourceAwsRamResourceShareStateRefreshFunc(conn *ram.RAM, resourceShareArn string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
request := &ram.GetResourceSharesInput{
ResourceShareArns: []*string{aws.String(resourceShareArn)},
ResourceOwner: aws.String(ram.ResourceOwnerSelf),
}

output, err := conn.GetResourceShares(request)

if err != nil {
return nil, ram.ResourceShareStatusFailed, err
}

if len(output.ResourceShares) == 0 {
return nil, ram.ResourceShareStatusDeleted, nil
}

resourceShare := output.ResourceShares[0]

return resourceShare, aws.StringValue(resourceShare.Status), nil
}
}
Loading

0 comments on commit 559e521

Please sign in to comment.