diff --git a/.changelog/pending.txt b/.changelog/pending.txt new file mode 100644 index 00000000000..a7b5ea42318 --- /dev/null +++ b/.changelog/pending.txt @@ -0,0 +1,11 @@ +```release-note:bug +resource/aws_ecr_lifecycle_policy: Handle read-after-create eventual consistency +``` + +```release-note:bug +resource/aws_ecr_repository: Handle read-after-create eventual consistency +``` + +```release-note:bug +resource/aws_ecr_repository_policy: Handle read-after-create eventual consistency +``` diff --git a/aws/internal/service/ecr/waiter/waiter.go b/aws/internal/service/ecr/waiter/waiter.go new file mode 100644 index 00000000000..33e7247ad36 --- /dev/null +++ b/aws/internal/service/ecr/waiter/waiter.go @@ -0,0 +1,10 @@ +package waiter + +import ( + "time" +) + +const ( + // Maximum amount of time to wait for ECR changes to propagate + PropagationTimeout = 2 * time.Minute +) diff --git a/aws/resource_aws_ecr_lifecycle_policy.go b/aws/resource_aws_ecr_lifecycle_policy.go index 9bde7e53c7e..06b5c32f55a 100644 --- a/aws/resource_aws_ecr_lifecycle_policy.go +++ b/aws/resource_aws_ecr_lifecycle_policy.go @@ -1,10 +1,16 @@ package aws import ( + "fmt" + "log" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecr" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecr/waiter" ) func resourceAwsEcrLifecyclePolicy() *schema.Resource { @@ -62,17 +68,46 @@ func resourceAwsEcrLifecyclePolicyRead(d *schema.ResourceData, meta interface{}) RepositoryName: aws.String(d.Id()), } - resp, err := conn.GetLifecyclePolicy(input) - if err != nil { - if isAWSErr(err, ecr.ErrCodeRepositoryNotFoundException, "") { - d.SetId("") - return nil + var resp *ecr.GetLifecyclePolicyOutput + + err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { + var err error + + resp, err = conn.GetLifecyclePolicy(input) + + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeLifecyclePolicyNotFoundException) { + return resource.RetryableError(err) } - if isAWSErr(err, ecr.ErrCodeLifecyclePolicyNotFoundException, "") { - d.SetId("") - return nil + + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { + return resource.RetryableError(err) } - return err + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeLifecyclePolicyNotFoundException) { + log.Printf("[WARN] ECR Lifecycle Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { + log.Printf("[WARN] ECR Lifecycle Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading ECR Lifecycle Policy (%s): %w", d.Id(), err) + } + + if resp == nil { + return fmt.Errorf("error reading ECR Lifecycle Policy (%s): empty response", d.Id()) } d.Set("repository", resp.RepositoryName) diff --git a/aws/resource_aws_ecr_repository.go b/aws/resource_aws_ecr_repository.go index d9de5e9601f..107f5738b5f 100644 --- a/aws/resource_aws_ecr_repository.go +++ b/aws/resource_aws_ecr_repository.go @@ -7,10 +7,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecr" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecr/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsEcrRepository() *schema.Resource { @@ -145,30 +148,38 @@ func resourceAwsEcrRepositoryRead(d *schema.ResourceData, meta interface{}) erro RepositoryNames: aws.StringSlice([]string{d.Id()}), } - var err error - err = resource.Retry(1*time.Minute, func() *resource.RetryError { + err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { + var err error + out, err = conn.DescribeRepositories(input) - if d.IsNewResource() && isAWSErr(err, ecr.ErrCodeRepositoryNotFoundException, "") { + + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { return resource.RetryableError(err) } + if err != nil { return resource.NonRetryableError(err) } + return nil }) - if isResourceTimeoutError(err) { + if tfresource.TimedOut(err) { out, err = conn.DescribeRepositories(input) } - if isAWSErr(err, ecr.ErrCodeRepositoryNotFoundException, "") { + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { log.Printf("[WARN] ECR Repository (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error reading ECR repository: %s", err) + return fmt.Errorf("error reading ECR Repository (%s): %w", d.Id(), err) + } + + if out == nil || len(out.Repositories) == 0 || out.Repositories[0] == nil { + return fmt.Errorf("error reading ECR Repository (%s): empty response", d.Id()) } repository := out.Repositories[0] diff --git a/aws/resource_aws_ecr_repository_policy.go b/aws/resource_aws_ecr_repository_policy.go index f5d9e92eef8..170cb40ed18 100644 --- a/aws/resource_aws_ecr_repository_policy.go +++ b/aws/resource_aws_ecr_repository_policy.go @@ -6,10 +6,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecr" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ecr/waiter" iamwaiter "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/iam/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsEcrRepositoryPolicy() *schema.Resource { @@ -83,18 +86,54 @@ func resourceAwsEcrRepositoryPolicyPut(d *schema.ResourceData, meta interface{}) func resourceAwsEcrRepositoryPolicyRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecrconn - log.Printf("[DEBUG] Reading repository policy %s", d.Id()) - out, err := conn.GetRepositoryPolicy(&ecr.GetRepositoryPolicyInput{ + input := &ecr.GetRepositoryPolicyInput{ RepositoryName: aws.String(d.Id()), + } + + var out *ecr.GetRepositoryPolicyOutput + + err := resource.Retry(waiter.PropagationTimeout, func() *resource.RetryError { + var err error + + out, err = conn.GetRepositoryPolicy(input) + + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { + return resource.RetryableError(err) + } + + if d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryPolicyNotFoundException) { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil }) + + if tfresource.TimedOut(err) { + out, err = conn.GetRepositoryPolicy(input) + } + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryNotFoundException) { + log.Printf("[WARN] ECR Repository Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, ecr.ErrCodeRepositoryPolicyNotFoundException) { + log.Printf("[WARN] ECR Repository Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { - if isAWSErr(err, ecr.ErrCodeRepositoryNotFoundException, "") || - isAWSErr(err, ecr.ErrCodeRepositoryPolicyNotFoundException, "") { - log.Printf("[WARN] ECR Repository Policy %s not found, removing", d.Id()) - d.SetId("") - return nil - } - return err + return fmt.Errorf("error reading ECR Repository Policy (%s): %w", d.Id(), err) + } + + if out == nil { + return fmt.Errorf("error reading ECR Repository Policy (%s): empty response", d.Id()) } log.Printf("[DEBUG] Received repository policy %s", out)