From f0faa3e60012c95a08b0895d05a2cbd94d802e58 Mon Sep 17 00:00:00 2001 From: Max Lund Date: Fri, 9 Sep 2022 11:01:38 -0500 Subject: [PATCH 01/14] add resource aws_sqs_queue_redrive_policy --- internal/provider/provider.go | 5 +- internal/service/acm/sweep.go | 2 +- internal/service/sqs/attribute_funcs.go | 147 +++++++++++++++ internal/service/sqs/find.go | 9 +- internal/service/sqs/queue.go | 1 + internal/service/sqs/queue_policy.go | 111 +---------- internal/service/sqs/queue_redrive_policy.go | 43 +++++ .../service/sqs/queue_redrive_policy_test.go | 178 ++++++++++++++++++ internal/service/sqs/status.go | 12 +- internal/service/sqs/strings.go | 14 ++ internal/service/sqs/wait.go | 27 ++- .../r/sqs_queue_redrive_policy.html.markdown | 59 ++++++ 12 files changed, 484 insertions(+), 124 deletions(-) create mode 100644 internal/service/sqs/attribute_funcs.go create mode 100644 internal/service/sqs/queue_redrive_policy.go create mode 100644 internal/service/sqs/queue_redrive_policy_test.go create mode 100644 internal/service/sqs/strings.go create mode 100644 website/docs/r/sqs_queue_redrive_policy.html.markdown diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c48d27fe3d7..9336724b6c4 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -2052,8 +2052,9 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_sns_topic_policy": sns.ResourceTopicPolicy(), "aws_sns_topic_subscription": sns.ResourceTopicSubscription(), - "aws_sqs_queue": sqs.ResourceQueue(), - "aws_sqs_queue_policy": sqs.ResourceQueuePolicy(), + "aws_sqs_queue": sqs.ResourceQueue(), + "aws_sqs_queue_policy": sqs.ResourceQueuePolicy(), + "aws_sqs_queue_redrive_policy": sqs.ResourceQueueRedrivePolicy(), "aws_ssm_activation": ssm.ResourceActivation(), "aws_ssm_association": ssm.ResourceAssociation(), diff --git a/internal/service/acm/sweep.go b/internal/service/acm/sweep.go index e12138944f6..9fe66134a6d 100644 --- a/internal/service/acm/sweep.go +++ b/internal/service/acm/sweep.go @@ -72,7 +72,7 @@ func sweepCertificates(region string) error { for _, iub := range output.Certificate.InUseBy { m[aws.StringValue(iub)[:77]] = "" } - for k, _ := range m { + for k := range m { log.Printf("[INFO] %s...", k) } continue diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go new file mode 100644 index 00000000000..1559e412805 --- /dev/null +++ b/internal/service/sqs/attribute_funcs.go @@ -0,0 +1,147 @@ +package sqs + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/sqs" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "log" +) + +func generateQueueAttributeUpsertFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + attrValue, err := structure.NormalizeJsonString(d.Get(getSchemaKey(attributeName)).(string)) + + if err != nil { + return diag.FromErr(fmt.Errorf("%s (%s) is invalid JSON: %w", attributeName, d.Get(getSchemaKey(attributeName)).(string), err)) + } + + var attributes map[string]string + + switch attributeName { + case sqs.QueueAttributeNamePolicy: + attributes = map[string]string{ + sqs.QueueAttributeNamePolicy: attrValue, + } + case sqs.QueueAttributeNameRedrivePolicy: + attributes = map[string]string{ + sqs.QueueAttributeNameRedrivePolicy: attrValue, + } + default: + return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) + } + + url := d.Get("queue_url").(string) + input := &sqs.SetQueueAttributesInput{ + Attributes: aws.StringMap(attributes), + QueueUrl: aws.String(url), + } + + log.Printf("[DEBUG] Setting SQS Queue Attribute '%s': %s", attributeName, input) + _, err = conn.SetQueueAttributesWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error setting SQS Queue Attribute '%s' (%s): %w", attributeName, url, err)) + } + + d.SetId(url) + + err = waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes) + + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for SQS Queue Attribute '%s' (%s) to be set: %w", attributeName, d.Id(), err)) + } + + return generateQueueAttributeReadFunc(attributeName)(ctx, d, meta) + } +} +func generateQueueAttributeReadFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + outputRaw, err := tfresource.RetryWhenNotFound(queueAttributeReadTimeout, func() (interface{}, error) { + return FindQueueAttributeByURL(ctx, conn, d.Id(), attributeName) + }) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] SQS Queue Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading SQS Queue Attribute '%s' (%s): %w", attributeName, d.Id(), err)) + } + + var attributeToSet string + switch attributeName { + case sqs.QueueAttributeNamePolicy: + attributeToSet, err = verify.PolicyToSet(d.Get(getSchemaKey(attributeName)).(string), outputRaw.(string)) + if err != nil { + return diag.FromErr(err) + } + case sqs.QueueAttributeNameRedrivePolicy: + if BytesEqual([]byte(d.Get(getSchemaKey(attributeName)).(string)), []byte(outputRaw.(string))) { + attributeToSet = d.Get(getSchemaKey(attributeName)).(string) + } else { + attributeToSet = outputRaw.(string) + } + default: + return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) + } + + d.Set(getSchemaKey(attributeName), attributeToSet) + + d.Set("queue_url", d.Id()) + + return nil + } +} + +func generateQueueAttributeDeleteFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + log.Printf("[DEBUG] Deleting SQS Queue Attribute '%s': %s", attributeName, d.Id()) + + var emptyAttributes map[string]string + switch attributeName { + case sqs.QueueAttributeNamePolicy: + emptyAttributes = queueEmptyPolicyAttributes + case sqs.QueueAttributeNameRedrivePolicy: + emptyAttributes = queueEmptyRedrivePolicyAttributes + default: + return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) + } + + _, err := conn.SetQueueAttributes(&sqs.SetQueueAttributesInput{ + Attributes: aws.StringMap(emptyAttributes), + QueueUrl: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting SQS Queue Attribute '%s' (%s): %w", attributeName, d.Id(), err)) + } + + err = waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), emptyAttributes) + + if err != nil { + return diag.FromErr(fmt.Errorf("error waiting for SQS Queue Attribute '%s' (%s) to delete: %w", attributeName, d.Id(), err)) + } + + return nil + } +} diff --git a/internal/service/sqs/find.go b/internal/service/sqs/find.go index 0ea79b75aa1..1cdf1cfadbd 100644 --- a/internal/service/sqs/find.go +++ b/internal/service/sqs/find.go @@ -1,6 +1,7 @@ package sqs import ( + "context" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" @@ -36,13 +37,13 @@ func FindQueueAttributesByURL(conn *sqs.SQS, url string) (map[string]string, err return aws.StringValueMap(output.Attributes), nil } -func FindQueuePolicyByURL(conn *sqs.SQS, url string) (string, error) { +func FindQueueAttributeByURL(ctx context.Context, conn *sqs.SQS, url string, attributeName string) (string, error) { input := &sqs.GetQueueAttributesInput{ - AttributeNames: aws.StringSlice([]string{sqs.QueueAttributeNamePolicy}), + AttributeNames: aws.StringSlice([]string{attributeName}), QueueUrl: aws.String(url), } - output, err := conn.GetQueueAttributes(input) + output, err := conn.GetQueueAttributesWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { return "", &resource.NotFoundError{ @@ -62,7 +63,7 @@ func FindQueuePolicyByURL(conn *sqs.SQS, url string) (string, error) { } } - v, ok := output.Attributes[sqs.QueueAttributeNamePolicy] + v, ok := output.Attributes[attributeName] if !ok || aws.StringValue(v) == "" { return "", &resource.NotFoundError{ diff --git a/internal/service/sqs/queue.go b/internal/service/sqs/queue.go index 4ef0792707c..432a10b701e 100644 --- a/internal/service/sqs/queue.go +++ b/internal/service/sqs/queue.go @@ -121,6 +121,7 @@ var ( "redrive_policy": { Type: schema.TypeString, Optional: true, + Computed: true, ValidateFunc: validation.StringIsJSON, StateFunc: func(v interface{}) string { json, _ := structure.NormalizeJsonString(v) diff --git a/internal/service/sqs/queue_policy.go b/internal/service/sqs/queue_policy.go index 9b898d9014c..1e7e40b4ee6 100644 --- a/internal/service/sqs/queue_policy.go +++ b/internal/service/sqs/queue_policy.go @@ -1,17 +1,10 @@ package sqs import ( - "fmt" - "log" - - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -24,16 +17,15 @@ var ( func ResourceQueuePolicy() *schema.Resource { //lintignore:R011 return &schema.Resource{ - Create: resourceQueuePolicyUpsert, - Read: resourceQueuePolicyRead, - Update: resourceQueuePolicyUpsert, - Delete: resourceQueuePolicyDelete, + CreateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNamePolicy), + ReadContext: generateQueueAttributeReadFunc(sqs.QueueAttributeNamePolicy), + UpdateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNamePolicy), + DeleteContext: generateQueueAttributeDeleteFunc(sqs.QueueAttributeNamePolicy), Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, MigrateState: QueuePolicyMigrateState, SchemaVersion: 1, - Schema: map[string]*schema.Schema{ "policy": { Type: schema.TypeString, @@ -54,96 +46,3 @@ func ResourceQueuePolicy() *schema.Resource { }, } } - -func resourceQueuePolicyUpsert(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).SQSConn - - policy, err := structure.NormalizeJsonString(d.Get("policy").(string)) - - if err != nil { - return fmt.Errorf("policy (%s) is invalid JSON: %w", d.Get("policy").(string), err) - } - - policyAttributes := map[string]string{ - sqs.QueueAttributeNamePolicy: policy, - } - - url := d.Get("queue_url").(string) - input := &sqs.SetQueueAttributesInput{ - Attributes: aws.StringMap(policyAttributes), - QueueUrl: aws.String(url), - } - - log.Printf("[DEBUG] Setting SQS Queue Policy: %s", input) - _, err = conn.SetQueueAttributes(input) - - if err != nil { - return fmt.Errorf("error setting SQS Queue Policy (%s): %w", url, err) - } - - d.SetId(url) - - err = waitQueueAttributesPropagated(conn, d.Id(), policyAttributes) - - if err != nil { - return fmt.Errorf("error waiting for SQS Queue Policy (%s) to be set: %w", d.Id(), err) - } - - return resourceQueuePolicyRead(d, meta) -} - -func resourceQueuePolicyRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).SQSConn - - outputRaw, err := tfresource.RetryWhenNotFound(queuePolicyReadTimeout, func() (interface{}, error) { - return FindQueuePolicyByURL(conn, d.Id()) - }) - - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] SQS Queue Policy (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("error reading SQS Queue Policy (%s): %w", d.Id(), err) - } - - policyToSet, err := verify.PolicyToSet(d.Get("policy").(string), outputRaw.(string)) - - if err != nil { - return err - } - - d.Set("policy", policyToSet) - - d.Set("queue_url", d.Id()) - - return nil -} - -func resourceQueuePolicyDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).SQSConn - - log.Printf("[DEBUG] Deleting SQS Queue Policy: %s", d.Id()) - _, err := conn.SetQueueAttributes(&sqs.SetQueueAttributesInput{ - Attributes: aws.StringMap(queueEmptyPolicyAttributes), - QueueUrl: aws.String(d.Id()), - }) - - if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { - return nil - } - - if err != nil { - return fmt.Errorf("error deleting SQS Queue Policy (%s): %w", d.Id(), err) - } - - err = waitQueueAttributesPropagated(conn, d.Id(), queueEmptyPolicyAttributes) - - if err != nil { - return fmt.Errorf("error waiting for SQS Queue Policy (%s) to delete: %w", d.Id(), err) - } - - return nil -} diff --git a/internal/service/sqs/queue_redrive_policy.go b/internal/service/sqs/queue_redrive_policy.go new file mode 100644 index 00000000000..ec913789217 --- /dev/null +++ b/internal/service/sqs/queue_redrive_policy.go @@ -0,0 +1,43 @@ +package sqs + +import ( + "github.com/aws/aws-sdk-go/service/sqs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var ( + queueEmptyRedrivePolicyAttributes = map[string]string{ + sqs.QueueAttributeNameRedrivePolicy: "", + } +) + +func ResourceQueueRedrivePolicy() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "redrive_policy": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + StateFunc: func(v interface{}) string { + json, _ := structure.NormalizeJsonString(v) + return json + }, + }, + "queue_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + SchemaVersion: 0, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + CreateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNameRedrivePolicy), + ReadContext: generateQueueAttributeReadFunc(sqs.QueueAttributeNameRedrivePolicy), + UpdateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNameRedrivePolicy), + DeleteContext: generateQueueAttributeDeleteFunc(sqs.QueueAttributeNameRedrivePolicy), + } +} diff --git a/internal/service/sqs/queue_redrive_policy_test.go b/internal/service/sqs/queue_redrive_policy_test.go new file mode 100644 index 00000000000..34bac2247a8 --- /dev/null +++ b/internal/service/sqs/queue_redrive_policy_test.go @@ -0,0 +1,178 @@ +package sqs_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/sqs" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfsqs "github.com/hashicorp/terraform-provider-aws/internal/service/sqs" +) + +func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedrivePolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + testAccCheckQueueExists(fmt.Sprintf("%s-ddl", queueResourceName), &queueAttributes), + resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccQueueRedrivePolicyConfig_basic(rName), + PlanOnly: true, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "redrive_policy", queueResourceName, "redrive_policy"), + ), + }, + }, + }) +} + +func TestAccSQSQueueRedrivePolicy_disappears(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedrivePolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + testAccCheckQueueExists(fmt.Sprintf("%s-ddl", queueResourceName), &queueAttributes), + acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueueRedrivePolicy(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccSQSQueueRedrivePolicy_Disappears_queue(t *testing.T) { + var queueAttributes map[string]string + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedrivePolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueue(), queueResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccSQSQueueRedrivePolicy_update(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedrivePolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccQueueRedrivePolicyConfig_updated(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), + ), + }, + }, + }) +} + +func testAccQueueRedrivePolicyConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_sqs_queue" "test" { + name = %[1]q +} + +resource "aws_sqs_queue" "test-ddl" { + name = "%[1]s-ddl" + redrive_allow_policy = jsonencode({ + redrivePermission = "byQueue", + sourceQueueArns = [aws_sqs_queue.test.arn] + }) +} + +resource "aws_sqs_queue_redrive_policy" "test" { + queue_url = aws_sqs_queue.test.id + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.test-ddl.arn + maxReceiveCount = 4 + }) +} +`, rName) +} + +func testAccQueueRedrivePolicyConfig_updated(rName string) string { + return fmt.Sprintf(` +resource "aws_sqs_queue" "test" { + name = %[1]q +} + +resource "aws_sqs_queue" "test-ddl" { + name = "%[1]s-ddl" + redrive_allow_policy = jsonencode({ + redrivePermission = "byQueue", + sourceQueueArns = [aws_sqs_queue.test.arn] + }) +} + +resource "aws_sqs_queue_redrive_policy" "test" { + queue_url = aws_sqs_queue.test.id + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.test-ddl.arn + maxReceiveCount = 2 + }) +} +`, rName) +} diff --git a/internal/service/sqs/status.go b/internal/service/sqs/status.go index 7c995b5da76..ff2b997b99f 100644 --- a/internal/service/sqs/status.go +++ b/internal/service/sqs/status.go @@ -42,7 +42,7 @@ func statusQueueAttributeState(conn *sqs.SQS, url string, expected map[string]st continue } - return queuePolicyStateNotEqual + return queueAttributeStateNotEqual } switch k { @@ -50,24 +50,24 @@ func statusQueueAttributeState(conn *sqs.SQS, url string, expected map[string]st equivalent, err := awspolicy.PoliciesAreEquivalent(g, e) if err != nil { - return queuePolicyStateNotEqual + return queueAttributeStateNotEqual } if !equivalent { - return queuePolicyStateNotEqual + return queueAttributeStateNotEqual } case sqs.QueueAttributeNameRedriveAllowPolicy, sqs.QueueAttributeNameRedrivePolicy: if !StringsEquivalent(g, e) { - return queuePolicyStateNotEqual + return queueAttributeStateNotEqual } default: if g != e { - return queuePolicyStateNotEqual + return queueAttributeStateNotEqual } } } - return queuePolicyStateEqual + return queueAttributeStateEqual } got, err := FindQueueAttributesByURL(conn, url) diff --git a/internal/service/sqs/strings.go b/internal/service/sqs/strings.go new file mode 100644 index 00000000000..3a9be3711d7 --- /dev/null +++ b/internal/service/sqs/strings.go @@ -0,0 +1,14 @@ +package sqs + +import "github.com/aws/aws-sdk-go/service/sqs" + +func getSchemaKey(attributeName string) string { + switch attributeName { + case sqs.QueueAttributeNamePolicy: + return "policy" + case sqs.QueueAttributeNameRedrivePolicy: + return "redrive_policy" + default: + return "" + } +} diff --git a/internal/service/sqs/wait.go b/internal/service/sqs/wait.go index da8cd44fd55..69ba02faf10 100644 --- a/internal/service/sqs/wait.go +++ b/internal/service/sqs/wait.go @@ -1,6 +1,7 @@ package sqs import ( + "context" "time" "github.com/aws/aws-sdk-go/service/sqs" @@ -22,18 +23,34 @@ const ( queueDeletedTimeout = 3 * time.Minute queueTagsTimeout = 60 * time.Second - queuePolicyReadTimeout = 20 * time.Second + queueAttributeReadTimeout = 20 * time.Second queueStateExists = "exists" - queuePolicyStateNotEqual = "notequal" - queuePolicyStateEqual = "equal" + queueAttributeStateNotEqual = "notequal" + queueAttributeStateEqual = "equal" ) +func waitQueueAttributesPropagatedWithContext(ctx context.Context, conn *sqs.SQS, url string, expected map[string]string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{queueAttributeStateNotEqual}, + Target: []string{queueAttributeStateEqual}, + Refresh: statusQueueAttributeState(conn, url, expected), + Timeout: queueAttributePropagationTimeout, + ContinuousTargetOccurence: 6, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering + MinTimeout: 5 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering + NotFoundChecks: 10, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering + } + + _, err := stateConf.WaitForStateContext(ctx) + + return err +} + func waitQueueAttributesPropagated(conn *sqs.SQS, url string, expected map[string]string) error { stateConf := &resource.StateChangeConf{ - Pending: []string{queuePolicyStateNotEqual}, - Target: []string{queuePolicyStateEqual}, + Pending: []string{queueAttributeStateNotEqual}, + Target: []string{queueAttributeStateEqual}, Refresh: statusQueueAttributeState(conn, url, expected), Timeout: queueAttributePropagationTimeout, ContinuousTargetOccurence: 6, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering diff --git a/website/docs/r/sqs_queue_redrive_policy.html.markdown b/website/docs/r/sqs_queue_redrive_policy.html.markdown new file mode 100644 index 00000000000..c3204e4ac88 --- /dev/null +++ b/website/docs/r/sqs_queue_redrive_policy.html.markdown @@ -0,0 +1,59 @@ +--- +subcategory: "SQS (Simple Queue)" +layout: "aws" +page_title: "AWS: aws_sqs_queue_redrive_policy" +description: |- +Provides a SQS Queue Redrive Policy resource. +--- + +# Resource: aws_sqs_queue_redrive_policy + +Allows you to set a redrive policy of an SQS Queue +while referencing ARN of the dead letter queue inside the redrive policy. + +This is useful when you want to set a dedicated +dead letter queue for a standard or FIFO queue, but need +the dead letter queue to exist before setting the redrive policy. + +## Example Usage + +```terraform +resource "aws_sqs_queue" "q" { + name = "examplequeue" +} + +resource "aws_sqs_queue" "ddl" { + name = "examplequeue-ddl" + redrive_allow_policy = jsonencode({ + redrivePermission = "byQueue", + sourceQueueArns = [aws_sqs_queue.q.arn] + }) +} + +resource "aws_sqs_queue_redrive_policy" "q" { + queue_url = aws_sqs_queue.q.id + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.ddl.arn + maxReceiveCount = 4 + }) +} +``` + +## Argument Reference + +The following arguments are supported: + +* `queue_url` - (Required) The URL of the SQS Queue to which to attach the policy +* `redrive_policy` - (Required) The JSON redrive policy for the SQS queue. Accepts two key/val pairs: `deadLetterTargetArn` and `maxReceiveCount`. Learn more in the [Amazon SQS dead-letter queues documentation](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html). + +## Attributes Reference + +No additional attributes are exported. + +## Import + +SQS Queue Redrive Policies can be imported using the queue URL, e.g., + +``` +$ terraform import aws_sqs_queue_redrive_policy.test https://queue.amazonaws.com/0123456789012/myqueue +``` From a2e6afc9106fefb689bd9cf4cd7aa37878048b2a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 17:14:35 -0400 Subject: [PATCH 02/14] Add CHANGELOG entry. --- .changelog/26733.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/26733.txt diff --git a/.changelog/26733.txt b/.changelog/26733.txt new file mode 100644 index 00000000000..6e47784b4c2 --- /dev/null +++ b/.changelog/26733.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_sqs_queue_redrive_policy +``` \ No newline at end of file From 83fe0fcfe05bf9fa1105f41fdf63e5e4f7209156 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 17:38:57 -0400 Subject: [PATCH 03/14] Some tidy up. --- internal/service/sqs/attribute_funcs.go | 62 +++++++------------- internal/service/sqs/queue_policy.go | 6 -- internal/service/sqs/queue_redrive_policy.go | 6 -- 3 files changed, 21 insertions(+), 53 deletions(-) diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go index 1559e412805..faef970cdd9 100644 --- a/internal/service/sqs/attribute_funcs.go +++ b/internal/service/sqs/attribute_funcs.go @@ -3,6 +3,8 @@ package sqs import ( "context" "fmt" + "log" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" @@ -12,9 +14,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" - "log" ) +type queueAttributeHandler struct{} + func generateQueueAttributeUpsertFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn @@ -25,61 +28,48 @@ func generateQueueAttributeUpsertFunc(attributeName string) func(ctx context.Con return diag.FromErr(fmt.Errorf("%s (%s) is invalid JSON: %w", attributeName, d.Get(getSchemaKey(attributeName)).(string), err)) } - var attributes map[string]string - - switch attributeName { - case sqs.QueueAttributeNamePolicy: - attributes = map[string]string{ - sqs.QueueAttributeNamePolicy: attrValue, - } - case sqs.QueueAttributeNameRedrivePolicy: - attributes = map[string]string{ - sqs.QueueAttributeNameRedrivePolicy: attrValue, - } - default: - return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) + attributes := map[string]string{ + attributeName: attrValue, } - url := d.Get("queue_url").(string) input := &sqs.SetQueueAttributesInput{ Attributes: aws.StringMap(attributes), QueueUrl: aws.String(url), } - log.Printf("[DEBUG] Setting SQS Queue Attribute '%s': %s", attributeName, input) + log.Printf("[DEBUG] Setting SQS Queue attributes: %s", input) _, err = conn.SetQueueAttributesWithContext(ctx, input) if err != nil { - return diag.FromErr(fmt.Errorf("error setting SQS Queue Attribute '%s' (%s): %w", attributeName, url, err)) + return diag.Errorf("setting SQS Queue (%s) attribute (%s): %s", url, attributeName, err) } d.SetId(url) - err = waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes) - - if err != nil { - return diag.FromErr(fmt.Errorf("error waiting for SQS Queue Attribute '%s' (%s) to be set: %w", attributeName, d.Id(), err)) + if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), attributeName, err) } return generateQueueAttributeReadFunc(attributeName)(ctx, d, meta) } } + func generateQueueAttributeReadFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn - outputRaw, err := tfresource.RetryWhenNotFound(queueAttributeReadTimeout, func() (interface{}, error) { + outputRaw, err := tfresource.RetryWhenNotFoundContext(ctx, queueAttributeReadTimeout, func() (interface{}, error) { return FindQueueAttributeByURL(ctx, conn, d.Id(), attributeName) }) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] SQS Queue Policy (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] SQS Queue (%s) attribute (%s) not found, removing from state", d.Id(), attributeName) d.SetId("") return nil } if err != nil { - return diag.FromErr(fmt.Errorf("error reading SQS Queue Attribute '%s' (%s): %w", attributeName, d.Id(), err)) + return diag.Errorf("reading SQS Queue (%s) attribute (%s): %s", d.Id(), attributeName, err) } var attributeToSet string @@ -111,20 +101,12 @@ func generateQueueAttributeDeleteFunc(attributeName string) func(ctx context.Con return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn - log.Printf("[DEBUG] Deleting SQS Queue Attribute '%s': %s", attributeName, d.Id()) - - var emptyAttributes map[string]string - switch attributeName { - case sqs.QueueAttributeNamePolicy: - emptyAttributes = queueEmptyPolicyAttributes - case sqs.QueueAttributeNameRedrivePolicy: - emptyAttributes = queueEmptyRedrivePolicyAttributes - default: - return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) + log.Printf("[DEBUG] Deleting SQS Queue (%s) attribute: %s", d.Id(), attributeName) + attributes := map[string]string{ + attributeName: "", } - _, err := conn.SetQueueAttributes(&sqs.SetQueueAttributesInput{ - Attributes: aws.StringMap(emptyAttributes), + Attributes: aws.StringMap(attributes), QueueUrl: aws.String(d.Id()), }) @@ -133,13 +115,11 @@ func generateQueueAttributeDeleteFunc(attributeName string) func(ctx context.Con } if err != nil { - return diag.FromErr(fmt.Errorf("error deleting SQS Queue Attribute '%s' (%s): %w", attributeName, d.Id(), err)) + return diag.Errorf("deleting SQS Queue (%s) attribute (%s): %s", d.Id(), attributeName, err) } - err = waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), emptyAttributes) - - if err != nil { - return diag.FromErr(fmt.Errorf("error waiting for SQS Queue Attribute '%s' (%s) to delete: %w", attributeName, d.Id(), err)) + if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) delete: %s", d.Id(), attributeName, err) } return nil diff --git a/internal/service/sqs/queue_policy.go b/internal/service/sqs/queue_policy.go index 1e7e40b4ee6..a8fd4fb0c30 100644 --- a/internal/service/sqs/queue_policy.go +++ b/internal/service/sqs/queue_policy.go @@ -8,12 +8,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -var ( - queueEmptyPolicyAttributes = map[string]string{ - sqs.QueueAttributeNamePolicy: "", - } -) - func ResourceQueuePolicy() *schema.Resource { //lintignore:R011 return &schema.Resource{ diff --git a/internal/service/sqs/queue_redrive_policy.go b/internal/service/sqs/queue_redrive_policy.go index ec913789217..0f51f9442b9 100644 --- a/internal/service/sqs/queue_redrive_policy.go +++ b/internal/service/sqs/queue_redrive_policy.go @@ -7,12 +7,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var ( - queueEmptyRedrivePolicyAttributes = map[string]string{ - sqs.QueueAttributeNameRedrivePolicy: "", - } -) - func ResourceQueueRedrivePolicy() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ From 538b63d4a16ad60b3b8ca6d6f918a69c20a3d396 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 18:03:49 -0400 Subject: [PATCH 04/14] Introduce 'queueAttributeHandler' type. --- internal/service/sqs/attribute_funcs.go | 86 ++++++++++++++++++++++++- internal/service/sqs/queue_policy.go | 14 ++-- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go index faef970cdd9..3c3ede7cb57 100644 --- a/internal/service/sqs/attribute_funcs.go +++ b/internal/service/sqs/attribute_funcs.go @@ -16,7 +16,91 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -type queueAttributeHandler struct{} +type queueAttributeHandler struct { + AttributeName string + SchemaKey string +} + +func (h *queueAttributeHandler) Create() schema.CreateContextFunc { + return h.upsert +} + +func (h *queueAttributeHandler) Read() schema.ReadContextFunc { + return h.read +} + +func (h *queueAttributeHandler) Update() schema.UpdateContextFunc { + return h.upsert +} + +func (h *queueAttributeHandler) Delete() schema.DeleteContextFunc { + return h.delete +} + +func (h *queueAttributeHandler) upsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + attrValue, err := structure.NormalizeJsonString(d.Get(h.SchemaKey).(string)) + + if err != nil { + return diag.FromErr(fmt.Errorf("%s (%s) is invalid JSON: %w", h.SchemaKey, d.Get(h.SchemaKey).(string), err)) + } + + attributes := map[string]string{ + h.AttributeName: attrValue, + } + url := d.Get("queue_url").(string) + input := &sqs.SetQueueAttributesInput{ + Attributes: aws.StringMap(attributes), + QueueUrl: aws.String(url), + } + + log.Printf("[DEBUG] Setting SQS Queue attributes: %s", input) + _, err = conn.SetQueueAttributesWithContext(ctx, input) + + if err != nil { + return diag.Errorf("setting SQS Queue (%s) attribute (%s): %s", url, h.AttributeName, err) + } + + d.SetId(url) + + if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), h.AttributeName, err) + } + + return h.read(ctx, d, meta) +} + +func (h *queueAttributeHandler) read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return nil +} + +func (h *queueAttributeHandler) delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + log.Printf("[DEBUG] Deleting SQS Queue (%s) attribute: %s", d.Id(), h.AttributeName) + attributes := map[string]string{ + h.AttributeName: "", + } + _, err := conn.SetQueueAttributes(&sqs.SetQueueAttributesInput{ + Attributes: aws.StringMap(attributes), + QueueUrl: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { + return nil + } + + if err != nil { + return diag.Errorf("deleting SQS Queue (%s) attribute (%s): %s", d.Id(), h.AttributeName, err) + } + + if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) delete: %s", d.Id(), h.AttributeName, err) + } + + return nil +} func generateQueueAttributeUpsertFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diff --git a/internal/service/sqs/queue_policy.go b/internal/service/sqs/queue_policy.go index a8fd4fb0c30..c231fbd4e4a 100644 --- a/internal/service/sqs/queue_policy.go +++ b/internal/service/sqs/queue_policy.go @@ -9,17 +9,22 @@ import ( ) func ResourceQueuePolicy() *schema.Resource { + h := &queueAttributeHandler{AttributeName: sqs.QueueAttributeNamePolicy, SchemaKey: "policy"} + //lintignore:R011 return &schema.Resource{ - CreateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNamePolicy), - ReadContext: generateQueueAttributeReadFunc(sqs.QueueAttributeNamePolicy), - UpdateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNamePolicy), - DeleteContext: generateQueueAttributeDeleteFunc(sqs.QueueAttributeNamePolicy), + CreateWithoutTimeout: h.Create(), + ReadWithoutTimeout: h.Read(), + UpdateWithoutTimeout: h.Update(), + DeleteWithoutTimeout: h.Delete(), + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + MigrateState: QueuePolicyMigrateState, SchemaVersion: 1, + Schema: map[string]*schema.Schema{ "policy": { Type: schema.TypeString, @@ -31,7 +36,6 @@ func ResourceQueuePolicy() *schema.Resource { return json }, }, - "queue_url": { Type: schema.TypeString, Required: true, From 74c2a6642ea883805d6cb5c471f449453bee22c7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 12 Sep 2022 18:36:50 -0400 Subject: [PATCH 05/14] Some simplifications. --- internal/service/sqs/attribute_funcs.go | 24 ++++-------------------- internal/service/sqs/queue_policy.go | 8 ++++---- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go index 3c3ede7cb57..77e71106928 100644 --- a/internal/service/sqs/attribute_funcs.go +++ b/internal/service/sqs/attribute_funcs.go @@ -21,23 +21,7 @@ type queueAttributeHandler struct { SchemaKey string } -func (h *queueAttributeHandler) Create() schema.CreateContextFunc { - return h.upsert -} - -func (h *queueAttributeHandler) Read() schema.ReadContextFunc { - return h.read -} - -func (h *queueAttributeHandler) Update() schema.UpdateContextFunc { - return h.upsert -} - -func (h *queueAttributeHandler) Delete() schema.DeleteContextFunc { - return h.delete -} - -func (h *queueAttributeHandler) upsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn attrValue, err := structure.NormalizeJsonString(d.Get(h.SchemaKey).(string)) @@ -68,14 +52,14 @@ func (h *queueAttributeHandler) upsert(ctx context.Context, d *schema.ResourceDa return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), h.AttributeName, err) } - return h.read(ctx, d, meta) + return h.Read(ctx, d, meta) } -func (h *queueAttributeHandler) read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func (h *queueAttributeHandler) Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { return nil } -func (h *queueAttributeHandler) delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func (h *queueAttributeHandler) Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn log.Printf("[DEBUG] Deleting SQS Queue (%s) attribute: %s", d.Id(), h.AttributeName) diff --git a/internal/service/sqs/queue_policy.go b/internal/service/sqs/queue_policy.go index c231fbd4e4a..f2bf987bdc7 100644 --- a/internal/service/sqs/queue_policy.go +++ b/internal/service/sqs/queue_policy.go @@ -13,10 +13,10 @@ func ResourceQueuePolicy() *schema.Resource { //lintignore:R011 return &schema.Resource{ - CreateWithoutTimeout: h.Create(), - ReadWithoutTimeout: h.Read(), - UpdateWithoutTimeout: h.Update(), - DeleteWithoutTimeout: h.Delete(), + CreateWithoutTimeout: h.Upsert, + ReadWithoutTimeout: h.Read, + UpdateWithoutTimeout: h.Upsert, + DeleteWithoutTimeout: h.Delete, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, From fdd0c4324572fa05622821262c3696c897bda744 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:22:36 -0400 Subject: [PATCH 06/14] Add 'ToSet' to 'queueAttributeHandler'. --- internal/service/sqs/attribute_funcs.go | 138 ++++--------------- internal/service/sqs/queue_policy.go | 6 +- internal/service/sqs/queue_redrive_policy.go | 32 +++-- internal/service/sqs/strings.go | 14 -- 4 files changed, 54 insertions(+), 136 deletions(-) delete mode 100644 internal/service/sqs/strings.go diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go index 77e71106928..38f1dface41 100644 --- a/internal/service/sqs/attribute_funcs.go +++ b/internal/service/sqs/attribute_funcs.go @@ -2,7 +2,6 @@ package sqs import ( "context" - "fmt" "log" "github.com/aws/aws-sdk-go/aws" @@ -13,12 +12,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" - "github.com/hashicorp/terraform-provider-aws/internal/verify" ) type queueAttributeHandler struct { AttributeName string SchemaKey string + ToSet func(string, string) (string, error) } func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -27,7 +26,7 @@ func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceDa attrValue, err := structure.NormalizeJsonString(d.Get(h.SchemaKey).(string)) if err != nil { - return diag.FromErr(fmt.Errorf("%s (%s) is invalid JSON: %w", h.SchemaKey, d.Get(h.SchemaKey).(string), err)) + return diag.Errorf("%s (%s) is invalid JSON: %s", h.SchemaKey, d.Get(h.SchemaKey).(string), err) } attributes := map[string]string{ @@ -56,6 +55,31 @@ func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceDa } func (h *queueAttributeHandler) Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).SQSConn + + outputRaw, err := tfresource.RetryWhenNotFoundContext(ctx, queueAttributeReadTimeout, func() (interface{}, error) { + return FindQueueAttributeByURL(ctx, conn, d.Id(), h.AttributeName) + }) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] SQS Queue (%s) attribute (%s) not found, removing from state", d.Id(), h.AttributeName) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading SQS Queue (%s) attribute (%s): %s", d.Id(), h.AttributeName, err) + } + + newValue, err := h.ToSet(d.Get(h.SchemaKey).(string), outputRaw.(string)) + + if err != nil { + return diag.FromErr(err) + } + + d.Set(h.SchemaKey, newValue) + d.Set("queue_url", d.Id()) + return nil } @@ -85,111 +109,3 @@ func (h *queueAttributeHandler) Delete(ctx context.Context, d *schema.ResourceDa return nil } - -func generateQueueAttributeUpsertFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).SQSConn - - attrValue, err := structure.NormalizeJsonString(d.Get(getSchemaKey(attributeName)).(string)) - - if err != nil { - return diag.FromErr(fmt.Errorf("%s (%s) is invalid JSON: %w", attributeName, d.Get(getSchemaKey(attributeName)).(string), err)) - } - - attributes := map[string]string{ - attributeName: attrValue, - } - url := d.Get("queue_url").(string) - input := &sqs.SetQueueAttributesInput{ - Attributes: aws.StringMap(attributes), - QueueUrl: aws.String(url), - } - - log.Printf("[DEBUG] Setting SQS Queue attributes: %s", input) - _, err = conn.SetQueueAttributesWithContext(ctx, input) - - if err != nil { - return diag.Errorf("setting SQS Queue (%s) attribute (%s): %s", url, attributeName, err) - } - - d.SetId(url) - - if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { - return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), attributeName, err) - } - - return generateQueueAttributeReadFunc(attributeName)(ctx, d, meta) - } -} - -func generateQueueAttributeReadFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).SQSConn - - outputRaw, err := tfresource.RetryWhenNotFoundContext(ctx, queueAttributeReadTimeout, func() (interface{}, error) { - return FindQueueAttributeByURL(ctx, conn, d.Id(), attributeName) - }) - - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] SQS Queue (%s) attribute (%s) not found, removing from state", d.Id(), attributeName) - d.SetId("") - return nil - } - - if err != nil { - return diag.Errorf("reading SQS Queue (%s) attribute (%s): %s", d.Id(), attributeName, err) - } - - var attributeToSet string - switch attributeName { - case sqs.QueueAttributeNamePolicy: - attributeToSet, err = verify.PolicyToSet(d.Get(getSchemaKey(attributeName)).(string), outputRaw.(string)) - if err != nil { - return diag.FromErr(err) - } - case sqs.QueueAttributeNameRedrivePolicy: - if BytesEqual([]byte(d.Get(getSchemaKey(attributeName)).(string)), []byte(outputRaw.(string))) { - attributeToSet = d.Get(getSchemaKey(attributeName)).(string) - } else { - attributeToSet = outputRaw.(string) - } - default: - return diag.FromErr(fmt.Errorf("%s is an invalid SQS Queue attribute name", attributeName)) - } - - d.Set(getSchemaKey(attributeName), attributeToSet) - - d.Set("queue_url", d.Id()) - - return nil - } -} - -func generateQueueAttributeDeleteFunc(attributeName string) func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).SQSConn - - log.Printf("[DEBUG] Deleting SQS Queue (%s) attribute: %s", d.Id(), attributeName) - attributes := map[string]string{ - attributeName: "", - } - _, err := conn.SetQueueAttributes(&sqs.SetQueueAttributesInput{ - Attributes: aws.StringMap(attributes), - QueueUrl: aws.String(d.Id()), - }) - - if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { - return nil - } - - if err != nil { - return diag.Errorf("deleting SQS Queue (%s) attribute (%s): %s", d.Id(), attributeName, err) - } - - if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { - return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) delete: %s", d.Id(), attributeName, err) - } - - return nil - } -} diff --git a/internal/service/sqs/queue_policy.go b/internal/service/sqs/queue_policy.go index f2bf987bdc7..fcd029a8083 100644 --- a/internal/service/sqs/queue_policy.go +++ b/internal/service/sqs/queue_policy.go @@ -9,7 +9,11 @@ import ( ) func ResourceQueuePolicy() *schema.Resource { - h := &queueAttributeHandler{AttributeName: sqs.QueueAttributeNamePolicy, SchemaKey: "policy"} + h := &queueAttributeHandler{ + AttributeName: sqs.QueueAttributeNamePolicy, + SchemaKey: "policy", + ToSet: verify.PolicyToSet, + } //lintignore:R011 return &schema.Resource{ diff --git a/internal/service/sqs/queue_redrive_policy.go b/internal/service/sqs/queue_redrive_policy.go index 0f51f9442b9..fd4884d44c1 100644 --- a/internal/service/sqs/queue_redrive_policy.go +++ b/internal/service/sqs/queue_redrive_policy.go @@ -8,8 +8,24 @@ import ( ) func ResourceQueueRedrivePolicy() *schema.Resource { + h := &queueAttributeHandler{ + AttributeName: sqs.QueueAttributeNameRedrivePolicy, + SchemaKey: "redrive_policy", + ToSet: func(old, new string) (string, error) { + if BytesEqual([]byte(old), []byte(new)) { + return old, nil + } + return new, nil + }, + } + return &schema.Resource{ Schema: map[string]*schema.Schema{ + "queue_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "redrive_policy": { Type: schema.TypeString, Required: true, @@ -19,19 +35,15 @@ func ResourceQueueRedrivePolicy() *schema.Resource { return json }, }, - "queue_url": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, }, - SchemaVersion: 0, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - CreateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNameRedrivePolicy), - ReadContext: generateQueueAttributeReadFunc(sqs.QueueAttributeNameRedrivePolicy), - UpdateContext: generateQueueAttributeUpsertFunc(sqs.QueueAttributeNameRedrivePolicy), - DeleteContext: generateQueueAttributeDeleteFunc(sqs.QueueAttributeNameRedrivePolicy), + + CreateWithoutTimeout: h.Upsert, + ReadWithoutTimeout: h.Read, + UpdateWithoutTimeout: h.Upsert, + DeleteWithoutTimeout: h.Delete, } } diff --git a/internal/service/sqs/strings.go b/internal/service/sqs/strings.go deleted file mode 100644 index 3a9be3711d7..00000000000 --- a/internal/service/sqs/strings.go +++ /dev/null @@ -1,14 +0,0 @@ -package sqs - -import "github.com/aws/aws-sdk-go/service/sqs" - -func getSchemaKey(attributeName string) string { - switch attributeName { - case sqs.QueueAttributeNamePolicy: - return "policy" - case sqs.QueueAttributeNameRedrivePolicy: - return "redrive_policy" - default: - return "" - } -} From 575edcdc99be105a495b99343ea68cc76d56077e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:30:11 -0400 Subject: [PATCH 07/14] 'test-ddl' -> 'test_ddl'. --- .../service/sqs/queue_redrive_policy_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/service/sqs/queue_redrive_policy_test.go b/internal/service/sqs/queue_redrive_policy_test.go index 34bac2247a8..0d7c6907423 100644 --- a/internal/service/sqs/queue_redrive_policy_test.go +++ b/internal/service/sqs/queue_redrive_policy_test.go @@ -27,7 +27,7 @@ func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), - testAccCheckQueueExists(fmt.Sprintf("%s-ddl", queueResourceName), &queueAttributes), + testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), ), }, @@ -63,7 +63,7 @@ func TestAccSQSQueueRedrivePolicy_disappears(t *testing.T) { Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), - testAccCheckQueueExists(fmt.Sprintf("%s-ddl", queueResourceName), &queueAttributes), + testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueueRedrivePolicy(), resourceName), ), ExpectNonEmptyPlan: true, @@ -135,8 +135,8 @@ resource "aws_sqs_queue" "test" { name = %[1]q } -resource "aws_sqs_queue" "test-ddl" { - name = "%[1]s-ddl" +resource "aws_sqs_queue" "test_ddl" { + name = "%[1]s_ddl" redrive_allow_policy = jsonencode({ redrivePermission = "byQueue", sourceQueueArns = [aws_sqs_queue.test.arn] @@ -146,7 +146,7 @@ resource "aws_sqs_queue" "test-ddl" { resource "aws_sqs_queue_redrive_policy" "test" { queue_url = aws_sqs_queue.test.id redrive_policy = jsonencode({ - deadLetterTargetArn = aws_sqs_queue.test-ddl.arn + deadLetterTargetArn = aws_sqs_queue.test_ddl.arn maxReceiveCount = 4 }) } @@ -159,8 +159,8 @@ resource "aws_sqs_queue" "test" { name = %[1]q } -resource "aws_sqs_queue" "test-ddl" { - name = "%[1]s-ddl" +resource "aws_sqs_queue" "test_ddl" { + name = "%[1]s_ddl" redrive_allow_policy = jsonencode({ redrivePermission = "byQueue", sourceQueueArns = [aws_sqs_queue.test.arn] @@ -170,7 +170,7 @@ resource "aws_sqs_queue" "test-ddl" { resource "aws_sqs_queue_redrive_policy" "test" { queue_url = aws_sqs_queue.test.id redrive_policy = jsonencode({ - deadLetterTargetArn = aws_sqs_queue.test-ddl.arn + deadLetterTargetArn = aws_sqs_queue.test_ddl.arn maxReceiveCount = 2 }) } From d6c61a049ababf918cd7e4be0fb44cb14a9b2fb7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:51:39 -0400 Subject: [PATCH 08/14] r/aws_sqs_queue: Switch to 'WithoutTimeout' CRUD handlers (#15090). --- internal/service/sqs/queue.go | 61 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/internal/service/sqs/queue.go b/internal/service/sqs/queue.go index 0b4420cf506..29afc12397e 100644 --- a/internal/service/sqs/queue.go +++ b/internal/service/sqs/queue.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" @@ -169,13 +170,15 @@ var ( func ResourceQueue() *schema.Resource { return &schema.Resource{ - Create: resourceQueueCreate, - Read: resourceQueueRead, - Update: resourceQueueUpdate, - Delete: resourceQueueDelete, + CreateWithoutTimeout: resourceQueueCreate, + ReadWithoutTimeout: resourceQueueRead, + UpdateWithoutTimeout: resourceQueueUpdate, + DeleteWithoutTimeout: resourceQueueDelete, + Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, + CustomizeDiff: customdiff.Sequence( resourceQueueCustomizeDiff, verify.SetTagsDiff, @@ -185,7 +188,7 @@ func ResourceQueue() *schema.Resource { } } -func resourceQueueCreate(d *schema.ResourceData, meta interface{}) error { +func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) @@ -205,7 +208,7 @@ func resourceQueueCreate(d *schema.ResourceData, meta interface{}) error { attributes, err := queueAttributeMap.ResourceDataToAPIAttributesCreate(d) if err != nil { - return err + return diag.FromErr(err) } input.Attributes = aws.StringMap(attributes) @@ -230,7 +233,7 @@ func resourceQueueCreate(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("failed creating SQS Queue (%s): %w", name, err) + return diag.Errorf("creating SQS Queue (%s): %s", name, err) } d.SetId(aws.StringValue(outputRaw.(*sqs.CreateQueueOutput).QueueUrl)) @@ -238,7 +241,7 @@ func resourceQueueCreate(d *schema.ResourceData, meta interface{}) error { err = waitQueueAttributesPropagated(conn, d.Id(), attributes) if err != nil { - return fmt.Errorf("error waiting for SQS Queue (%s) attributes to create: %w", d.Id(), err) + return diag.Errorf("waiting for SQS Queue (%s) attributes create: %s", d.Id(), err) } // Only post-create tagging supported in some partitions @@ -248,18 +251,18 @@ func resourceQueueCreate(d *schema.ResourceData, meta interface{}) error { if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && verify.ErrorISOUnsupported(conn.PartitionID, err) { // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) log.Printf("[WARN] failed adding tags after create for SQS Queue (%s): %s", d.Id(), err) - return resourceQueueRead(d, meta) + return resourceQueueRead(ctx, d, meta) } if err != nil { - return fmt.Errorf("failed adding tags after create for SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("adding tags after create to SQS Queue (%s): %s", d.Id(), err) } } - return resourceQueueRead(d, meta) + return resourceQueueRead(ctx, d, meta) } -func resourceQueueRead(d *schema.ResourceData, meta interface{}) error { +func resourceQueueRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig @@ -275,13 +278,13 @@ func resourceQueueRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("error reading SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("reading SQS Queue (%s): %s", d.Id(), err) } name, err := QueueNameFromURL(d.Id()) if err != nil { - return err + return diag.FromErr(err) } output := outputRaw.(map[string]string) @@ -289,7 +292,7 @@ func resourceQueueRead(d *schema.ResourceData, meta interface{}) error { err = queueAttributeMap.APIAttributesToResourceData(output, d) if err != nil { - return err + return diag.FromErr(err) } // Backwards compatibility: https://github.com/hashicorp/terraform-provider-aws/issues/19786. @@ -316,31 +319,31 @@ func resourceQueueRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("failed listing tags for SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("listing tags for SQS Queue (%s): %s", d.Id(), err) } tags := outputRaw.(tftags.KeyValueTags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceQueueUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceQueueUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn if d.HasChangesExcept("tags", "tags_all") { attributes, err := queueAttributeMap.ResourceDataToAPIAttributesUpdate(d) if err != nil { - return err + return diag.FromErr(err) } input := &sqs.SetQueueAttributesInput{ @@ -352,13 +355,13 @@ func resourceQueueUpdate(d *schema.ResourceData, meta interface{}) error { _, err = conn.SetQueueAttributes(input) if err != nil { - return fmt.Errorf("error updating SQS Queue (%s) attributes: %w", d.Id(), err) + return diag.Errorf("updating SQS Queue (%s) attributes: %s", d.Id(), err) } err = waitQueueAttributesPropagated(conn, d.Id(), attributes) if err != nil { - return fmt.Errorf("error waiting for SQS Queue (%s) attributes to update: %w", d.Id(), err) + return diag.Errorf("waiting for SQS Queue (%s) attributes update: %s", d.Id(), err) } } @@ -369,18 +372,18 @@ func resourceQueueUpdate(d *schema.ResourceData, meta interface{}) error { if verify.ErrorISOUnsupported(conn.PartitionID, err) { // Some partitions may not support tagging, giving error log.Printf("[WARN] failed updating tags for SQS Queue (%s): %s", d.Id(), err) - return resourceQueueRead(d, meta) + return resourceQueueRead(ctx, d, meta) } if err != nil { - return fmt.Errorf("failed updating tags for SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("updating tags for SQS Queue (%s): %s", d.Id(), err) } } - return resourceQueueRead(d, meta) + return resourceQueueRead(ctx, d, meta) } -func resourceQueueDelete(d *schema.ResourceData, meta interface{}) error { +func resourceQueueDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn log.Printf("[DEBUG] Deleting SQS Queue: %s", d.Id()) @@ -393,13 +396,13 @@ func resourceQueueDelete(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("error deleting SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("deleting SQS Queue (%s): %s", d.Id(), err) } err = waitQueueDeleted(conn, d.Id()) if err != nil { - return fmt.Errorf("error waiting for SQS Queue (%s) to delete: %w", d.Id(), err) + return diag.Errorf("waiting for SQS Queue (%s) delete: %s", d.Id(), err) } return nil From 94d0b25189d98387a269969ef4cdd668467169e5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:54:53 -0400 Subject: [PATCH 09/14] d/aws_sqs_queue: Alphabetize attributes. --- internal/service/sqs/queue_data_source.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/service/sqs/queue_data_source.go b/internal/service/sqs/queue_data_source.go index 9cd154007a3..1d9bd2d16c2 100644 --- a/internal/service/sqs/queue_data_source.go +++ b/internal/service/sqs/queue_data_source.go @@ -15,20 +15,21 @@ import ( func DataSourceQueue() *schema.Resource { return &schema.Resource{ Read: dataSourceQueueRead, + Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, "arn": { Type: schema.TypeString, Computed: true, }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "tags": tftags.TagsSchemaComputed(), "url": { Type: schema.TypeString, Computed: true, }, - "tags": tftags.TagsSchemaComputed(), }, } } From d39186f5e68add2d3da7176719f7343daa31783c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 08:58:10 -0400 Subject: [PATCH 10/14] d/aws_sqs_queue: Switch to 'WithoutTimeout' CRUD handlers (#15090). --- internal/service/sqs/queue_data_source.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/internal/service/sqs/queue_data_source.go b/internal/service/sqs/queue_data_source.go index 1d9bd2d16c2..294ac9dfb4b 100644 --- a/internal/service/sqs/queue_data_source.go +++ b/internal/service/sqs/queue_data_source.go @@ -1,11 +1,12 @@ package sqs import ( - "fmt" + "context" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -14,7 +15,7 @@ import ( func DataSourceQueue() *schema.Resource { return &schema.Resource{ - Read: dataSourceQueueRead, + ReadWithoutTimeout: dataSourceQueueRead, Schema: map[string]*schema.Schema{ "arn": { @@ -34,34 +35,35 @@ func DataSourceQueue() *schema.Resource { } } -func dataSourceQueueRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceQueueRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).SQSConn ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig name := d.Get("name").(string) - urlOutput, err := conn.GetQueueUrl(&sqs.GetQueueUrlInput{ + urlOutput, err := conn.GetQueueUrlWithContext(ctx, &sqs.GetQueueUrlInput{ QueueName: aws.String(name), }) + if err != nil || urlOutput.QueueUrl == nil { - return fmt.Errorf("Error getting queue URL: %w", err) + return diag.Errorf("reading SQS Queue (%s) URL: %s", name, err) } queueURL := aws.StringValue(urlOutput.QueueUrl) - attributesOutput, err := conn.GetQueueAttributes(&sqs.GetQueueAttributesInput{ + attributesOutput, err := conn.GetQueueAttributesWithContext(ctx, &sqs.GetQueueAttributesInput{ QueueUrl: aws.String(queueURL), AttributeNames: []*string{aws.String(sqs.QueueAttributeNameQueueArn)}, }) if err != nil { - return fmt.Errorf("Error getting queue attributes: %w", err) + return diag.Errorf("reading SQS Queue (%s) attributes: %s", queueURL, err) } d.Set("arn", attributesOutput.Attributes[sqs.QueueAttributeNameQueueArn]) d.Set("url", queueURL) d.SetId(queueURL) - tags, err := ListTags(conn, queueURL) + tags, err := ListTagsWithContext(ctx, conn, queueURL) if verify.ErrorISOUnsupported(conn.PartitionID, err) { // Some partitions may not support tagging, giving error @@ -70,11 +72,11 @@ func dataSourceQueueRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("failed listing tags for SQS Queue (%s): %w", d.Id(), err) + return diag.Errorf("listing tags for SQS Queue (%s): %s", d.Id(), err) } if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return diag.Errorf("setting tags: %s", err) } return nil From 910ce31167cef89b460d0bd44528cf85c2a8b6ea Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 09:10:59 -0400 Subject: [PATCH 11/14] SQS: Propagate context. --- internal/service/sqs/attribute_funcs.go | 4 ++-- internal/service/sqs/find.go | 21 ++++++----------- internal/service/sqs/queue.go | 30 ++++++++++++------------- internal/service/sqs/queue_test.go | 5 +++-- internal/service/sqs/status.go | 9 ++++---- internal/service/sqs/wait.go | 26 +++++---------------- 6 files changed, 37 insertions(+), 58 deletions(-) diff --git a/internal/service/sqs/attribute_funcs.go b/internal/service/sqs/attribute_funcs.go index 38f1dface41..440986194f5 100644 --- a/internal/service/sqs/attribute_funcs.go +++ b/internal/service/sqs/attribute_funcs.go @@ -47,7 +47,7 @@ func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceDa d.SetId(url) - if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes); err != nil { return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), h.AttributeName, err) } @@ -103,7 +103,7 @@ func (h *queueAttributeHandler) Delete(ctx context.Context, d *schema.ResourceDa return diag.Errorf("deleting SQS Queue (%s) attribute (%s): %s", d.Id(), h.AttributeName, err) } - if err := waitQueueAttributesPropagatedWithContext(ctx, conn, d.Id(), attributes); err != nil { + if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes); err != nil { return diag.Errorf("waiting for SQS Queue (%s) attribute (%s) delete: %s", d.Id(), h.AttributeName, err) } diff --git a/internal/service/sqs/find.go b/internal/service/sqs/find.go index 1cdf1cfadbd..c036e1d4d90 100644 --- a/internal/service/sqs/find.go +++ b/internal/service/sqs/find.go @@ -2,19 +2,21 @@ package sqs import ( "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sqs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func FindQueueAttributesByURL(conn *sqs.SQS, url string) (map[string]string, error) { +func FindQueueAttributesByURL(ctx context.Context, conn *sqs.SQS, url string) (map[string]string, error) { input := &sqs.GetQueueAttributesInput{ AttributeNames: aws.StringSlice([]string{sqs.QueueAttributeNameAll}), QueueUrl: aws.String(url), } - output, err := conn.GetQueueAttributes(input) + output, err := conn.GetQueueAttributesWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, sqs.ErrCodeQueueDoesNotExist) { return nil, &resource.NotFoundError{ @@ -28,10 +30,7 @@ func FindQueueAttributesByURL(conn *sqs.SQS, url string) (map[string]string, err } if output == nil || output.Attributes == nil { - return nil, &resource.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } + return nil, tfresource.NewEmptyResultError(input) } return aws.StringValueMap(output.Attributes), nil @@ -57,19 +56,13 @@ func FindQueueAttributeByURL(ctx context.Context, conn *sqs.SQS, url string, att } if output == nil || output.Attributes == nil { - return "", &resource.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } + return "", tfresource.NewEmptyResultError(input) } v, ok := output.Attributes[attributeName] if !ok || aws.StringValue(v) == "" { - return "", &resource.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } + return "", tfresource.NewEmptyResultError(input) } return aws.StringValue(v), nil diff --git a/internal/service/sqs/queue.go b/internal/service/sqs/queue.go index 29afc12397e..41b95cad23b 100644 --- a/internal/service/sqs/queue.go +++ b/internal/service/sqs/queue.go @@ -218,8 +218,8 @@ func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta inter } log.Printf("[DEBUG] Creating SQS Queue: %s", input) - outputRaw, err := tfresource.RetryWhenAWSErrCodeEquals(queueCreatedTimeout, func() (interface{}, error) { - return conn.CreateQueue(input) + outputRaw, err := tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, queueCreatedTimeout, func() (interface{}, error) { + return conn.CreateQueueWithContext(ctx, input) }, sqs.ErrCodeQueueDeletedRecently) // Some partitions may not support tag-on-create @@ -227,8 +227,8 @@ func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta inter log.Printf("[WARN] failed creating SQS Queue (%s) with tags: %s. Trying create without tags.", name, err) input.Tags = nil - outputRaw, err = tfresource.RetryWhenAWSErrCodeEquals(queueCreatedTimeout, func() (interface{}, error) { - return conn.CreateQueue(input) + outputRaw, err = tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, queueCreatedTimeout, func() (interface{}, error) { + return conn.CreateQueueWithContext(ctx, input) }, sqs.ErrCodeQueueDeletedRecently) } @@ -238,7 +238,7 @@ func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta inter d.SetId(aws.StringValue(outputRaw.(*sqs.CreateQueueOutput).QueueUrl)) - err = waitQueueAttributesPropagated(conn, d.Id(), attributes) + err = waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes) if err != nil { return diag.Errorf("waiting for SQS Queue (%s) attributes create: %s", d.Id(), err) @@ -246,7 +246,7 @@ func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta inter // Only post-create tagging supported in some partitions if input.Tags == nil && len(tags) > 0 { - err := UpdateTags(conn, d.Id(), nil, tags) + err := UpdateTagsWithContext(ctx, conn, d.Id(), nil, tags) if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && verify.ErrorISOUnsupported(conn.PartitionID, err) { // if default tags only, log and continue (i.e., should error if explicitly setting tags and they can't be) @@ -267,8 +267,8 @@ func resourceQueueRead(ctx context.Context, d *schema.ResourceData, meta interfa defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - outputRaw, err := tfresource.RetryWhenNotFound(queueReadTimeout, func() (interface{}, error) { - return FindQueueAttributesByURL(conn, d.Id()) + outputRaw, err := tfresource.RetryWhenNotFoundContext(ctx, queueReadTimeout, func() (interface{}, error) { + return FindQueueAttributesByURL(ctx, conn, d.Id()) }) if !d.IsNewResource() && tfresource.NotFound(err) { @@ -308,8 +308,8 @@ func resourceQueueRead(ctx context.Context, d *schema.ResourceData, meta interfa } d.Set("url", d.Id()) - outputRaw, err = tfresource.RetryWhenAWSErrCodeEquals(queueTagsTimeout, func() (interface{}, error) { - return ListTags(conn, d.Id()) + outputRaw, err = tfresource.RetryWhenAWSErrCodeEqualsContext(ctx, queueTagsTimeout, func() (interface{}, error) { + return ListTagsWithContext(ctx, conn, d.Id()) }, sqs.ErrCodeQueueDoesNotExist) if verify.ErrorISOUnsupported(conn.PartitionID, err) { @@ -352,13 +352,13 @@ func resourceQueueUpdate(ctx context.Context, d *schema.ResourceData, meta inter } log.Printf("[DEBUG] Updating SQS Queue: %s", input) - _, err = conn.SetQueueAttributes(input) + _, err = conn.SetQueueAttributesWithContext(ctx, input) if err != nil { return diag.Errorf("updating SQS Queue (%s) attributes: %s", d.Id(), err) } - err = waitQueueAttributesPropagated(conn, d.Id(), attributes) + err = waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes) if err != nil { return diag.Errorf("waiting for SQS Queue (%s) attributes update: %s", d.Id(), err) @@ -367,7 +367,7 @@ func resourceQueueUpdate(ctx context.Context, d *schema.ResourceData, meta inter if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - err := UpdateTags(conn, d.Id(), o, n) + err := UpdateTagsWithContext(ctx, conn, d.Id(), o, n) if verify.ErrorISOUnsupported(conn.PartitionID, err) { // Some partitions may not support tagging, giving error @@ -387,7 +387,7 @@ func resourceQueueDelete(ctx context.Context, d *schema.ResourceData, meta inter conn := meta.(*conns.AWSClient).SQSConn log.Printf("[DEBUG] Deleting SQS Queue: %s", d.Id()) - _, err := conn.DeleteQueue(&sqs.DeleteQueueInput{ + _, err := conn.DeleteQueueWithContext(ctx, &sqs.DeleteQueueInput{ QueueUrl: aws.String(d.Id()), }) @@ -399,7 +399,7 @@ func resourceQueueDelete(ctx context.Context, d *schema.ResourceData, meta inter return diag.Errorf("deleting SQS Queue (%s): %s", d.Id(), err) } - err = waitQueueDeleted(conn, d.Id()) + err = waitQueueDeleted(ctx, conn, d.Id()) if err != nil { return diag.Errorf("waiting for SQS Queue (%s) delete: %s", d.Id(), err) diff --git a/internal/service/sqs/queue_test.go b/internal/service/sqs/queue_test.go index a9c9af15abf..8ecffc7eff2 100644 --- a/internal/service/sqs/queue_test.go +++ b/internal/service/sqs/queue_test.go @@ -1,6 +1,7 @@ package sqs_test import ( + "context" "fmt" "regexp" "strconv" @@ -770,7 +771,7 @@ func testAccCheckQueueExists(resourceName string, v *map[string]string) resource conn := acctest.Provider.Meta().(*conns.AWSClient).SQSConn - output, err := tfsqs.FindQueueAttributesByURL(conn, rs.Primary.ID) + output, err := tfsqs.FindQueueAttributesByURL(context.Background(), conn, rs.Primary.ID) if err != nil { return err @@ -790,7 +791,7 @@ func testAccCheckQueueDestroy(s *terraform.State) error { continue } - _, err := tfsqs.FindQueueAttributesByURL(conn, rs.Primary.ID) + _, err := tfsqs.FindQueueAttributesByURL(context.Background(), conn, rs.Primary.ID) if tfresource.NotFound(err) { continue diff --git a/internal/service/sqs/status.go b/internal/service/sqs/status.go index ff2b997b99f..c2547f6efee 100644 --- a/internal/service/sqs/status.go +++ b/internal/service/sqs/status.go @@ -1,6 +1,7 @@ package sqs import ( + "context" "strconv" "github.com/aws/aws-sdk-go/service/sqs" @@ -9,9 +10,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func statusQueueState(conn *sqs.SQS, url string) resource.StateRefreshFunc { +func statusQueueState(ctx context.Context, conn *sqs.SQS, url string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindQueueAttributesByURL(conn, url) + output, err := FindQueueAttributesByURL(ctx, conn, url) if tfresource.NotFound(err) { return nil, "", nil @@ -25,7 +26,7 @@ func statusQueueState(conn *sqs.SQS, url string) resource.StateRefreshFunc { } } -func statusQueueAttributeState(conn *sqs.SQS, url string, expected map[string]string) resource.StateRefreshFunc { +func statusQueueAttributeState(ctx context.Context, conn *sqs.SQS, url string, expected map[string]string) resource.StateRefreshFunc { return func() (interface{}, string, error) { attributesMatch := func(got map[string]string) string { for k, e := range expected { @@ -70,7 +71,7 @@ func statusQueueAttributeState(conn *sqs.SQS, url string, expected map[string]st return queueAttributeStateEqual } - got, err := FindQueueAttributesByURL(conn, url) + got, err := FindQueueAttributesByURL(ctx, conn, url) if tfresource.NotFound(err) { return nil, "", nil diff --git a/internal/service/sqs/wait.go b/internal/service/sqs/wait.go index 69ba02faf10..20d62e453c1 100644 --- a/internal/service/sqs/wait.go +++ b/internal/service/sqs/wait.go @@ -31,11 +31,11 @@ const ( queueAttributeStateEqual = "equal" ) -func waitQueueAttributesPropagatedWithContext(ctx context.Context, conn *sqs.SQS, url string, expected map[string]string) error { +func waitQueueAttributesPropagated(ctx context.Context, conn *sqs.SQS, url string, expected map[string]string) error { stateConf := &resource.StateChangeConf{ Pending: []string{queueAttributeStateNotEqual}, Target: []string{queueAttributeStateEqual}, - Refresh: statusQueueAttributeState(conn, url, expected), + Refresh: statusQueueAttributeState(ctx, conn, url, expected), Timeout: queueAttributePropagationTimeout, ContinuousTargetOccurence: 6, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering MinTimeout: 5 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering @@ -47,34 +47,18 @@ func waitQueueAttributesPropagatedWithContext(ctx context.Context, conn *sqs.SQS return err } -func waitQueueAttributesPropagated(conn *sqs.SQS, url string, expected map[string]string) error { - stateConf := &resource.StateChangeConf{ - Pending: []string{queueAttributeStateNotEqual}, - Target: []string{queueAttributeStateEqual}, - Refresh: statusQueueAttributeState(conn, url, expected), - Timeout: queueAttributePropagationTimeout, - ContinuousTargetOccurence: 6, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering - MinTimeout: 5 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering - NotFoundChecks: 10, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering - } - - _, err := stateConf.WaitForState() - - return err -} - -func waitQueueDeleted(conn *sqs.SQS, url string) error { +func waitQueueDeleted(ctx context.Context, conn *sqs.SQS, url string) error { stateConf := &resource.StateChangeConf{ Pending: []string{queueStateExists}, Target: []string{}, - Refresh: statusQueueState(conn, url), + Refresh: statusQueueState(ctx, conn, url), Timeout: queueDeletedTimeout, ContinuousTargetOccurence: 15, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering MinTimeout: 3 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering NotFoundChecks: 5, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering } - _, err := stateConf.WaitForState() + _, err := stateConf.WaitForStateContext(ctx) return err } From a1e52804e6e9fcef6dc00fe295ca539fa1ff09c1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 09:30:56 -0400 Subject: [PATCH 12/14] r/aws_sqs_queue_redrive_allow_policy: New resource. --- .changelog/26733.txt | 4 + internal/provider/provider.go | 7 +- internal/service/sqs/queue.go | 1 + .../service/sqs/queue_redrive_allow_policy.go | 49 +++++ .../sqs/queue_redrive_allow_policy_test.go | 175 ++++++++++++++++++ .../service/sqs/queue_redrive_policy_test.go | 12 +- ...s_queue_redrive_allow_policy.html.markdown | 56 ++++++ 7 files changed, 295 insertions(+), 9 deletions(-) create mode 100644 internal/service/sqs/queue_redrive_allow_policy.go create mode 100644 internal/service/sqs/queue_redrive_allow_policy_test.go create mode 100644 website/docs/r/sqs_queue_redrive_allow_policy.html.markdown diff --git a/.changelog/26733.txt b/.changelog/26733.txt index 6e47784b4c2..abef3adaeed 100644 --- a/.changelog/26733.txt +++ b/.changelog/26733.txt @@ -1,3 +1,7 @@ ```release-note:new-resource aws_sqs_queue_redrive_policy +``` + +```release-note:new-resource +aws_sqs_queue_redrive_allow_policy ``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index cab32641c15..5bf75c2a60f 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -2054,9 +2054,10 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_sns_topic_policy": sns.ResourceTopicPolicy(), "aws_sns_topic_subscription": sns.ResourceTopicSubscription(), - "aws_sqs_queue": sqs.ResourceQueue(), - "aws_sqs_queue_policy": sqs.ResourceQueuePolicy(), - "aws_sqs_queue_redrive_policy": sqs.ResourceQueueRedrivePolicy(), + "aws_sqs_queue": sqs.ResourceQueue(), + "aws_sqs_queue_policy": sqs.ResourceQueuePolicy(), + "aws_sqs_queue_redrive_allow_policy": sqs.ResourceQueueRedriveAllowPolicy(), + "aws_sqs_queue_redrive_policy": sqs.ResourceQueueRedrivePolicy(), "aws_ssm_activation": ssm.ResourceActivation(), "aws_ssm_association": ssm.ResourceAssociation(), diff --git a/internal/service/sqs/queue.go b/internal/service/sqs/queue.go index 41b95cad23b..d04692b7205 100644 --- a/internal/service/sqs/queue.go +++ b/internal/service/sqs/queue.go @@ -113,6 +113,7 @@ var ( "redrive_allow_policy": { Type: schema.TypeString, Optional: true, + Computed: true, ValidateFunc: validation.StringIsJSON, StateFunc: func(v interface{}) string { json, _ := structure.NormalizeJsonString(v) diff --git a/internal/service/sqs/queue_redrive_allow_policy.go b/internal/service/sqs/queue_redrive_allow_policy.go new file mode 100644 index 00000000000..706a108fcdc --- /dev/null +++ b/internal/service/sqs/queue_redrive_allow_policy.go @@ -0,0 +1,49 @@ +package sqs + +import ( + "github.com/aws/aws-sdk-go/service/sqs" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceQueueRedriveAllowPolicy() *schema.Resource { + h := &queueAttributeHandler{ + AttributeName: sqs.QueueAttributeNameRedriveAllowPolicy, + SchemaKey: "redrive_allow_policy", + ToSet: func(old, new string) (string, error) { + if BytesEqual([]byte(old), []byte(new)) { + return old, nil + } + return new, nil + }, + } + + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "queue_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "redrive_allow_policy": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + StateFunc: func(v interface{}) string { + json, _ := structure.NormalizeJsonString(v) + return json + }, + }, + }, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + CreateWithoutTimeout: h.Upsert, + ReadWithoutTimeout: h.Read, + UpdateWithoutTimeout: h.Upsert, + DeleteWithoutTimeout: h.Delete, + } +} diff --git a/internal/service/sqs/queue_redrive_allow_policy_test.go b/internal/service/sqs/queue_redrive_allow_policy_test.go new file mode 100644 index 00000000000..5da190d5e6d --- /dev/null +++ b/internal/service/sqs/queue_redrive_allow_policy_test.go @@ -0,0 +1,175 @@ +package sqs_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/sqs" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfsqs "github.com/hashicorp/terraform-provider-aws/internal/service/sqs" +) + +func TestAccSQSQueueRedriveAllowPolicy_basic(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_allow_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + resource.TestCheckResourceAttrSet(resourceName, "redrive_allow_policy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + PlanOnly: true, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "redrive_allow_policy", queueResourceName, "redrive_allow_policy"), + ), + }, + }, + }) +} + +func TestAccSQSQueueRedriveAllowPolicy_disappears(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_allow_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueueRedriveAllowPolicy(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccSQSQueueRedriveAllowPolicy_Disappears_queue(t *testing.T) { + var queueAttributes map[string]string + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueue(), queueResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccSQSQueueRedriveAllowPolicy_update(t *testing.T) { + var queueAttributes map[string]string + resourceName := "aws_sqs_queue_redrive_allow_policy.test" + queueResourceName := "aws_sqs_queue.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, sqs.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckQueueDestroy, + Steps: []resource.TestStep{ + { + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckQueueExists(queueResourceName, &queueAttributes), + resource.TestCheckResourceAttrSet(resourceName, "redrive_allow_policy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccQueueRedriveAllowPolicyConfig_updated(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "redrive_allow_policy"), + ), + }, + }, + }) +} + +func testAccQueueRedriveAllowPolicyConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_sqs_queue" "test" { + name = %[1]q +} + +resource "aws_sqs_queue" "test_src" { + name = "%[1]s_src" + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.test.arn + maxReceiveCount = 4 + }) +} + +resource "aws_sqs_queue_redrive_allow_policy" "test" { + queue_url = aws_sqs_queue.test.id + redrive_allow_policy = jsonencode({ + redrivePermission = "byQueue", + sourceQueueArns = [aws_sqs_queue.test_src.arn] + }) +} +`, rName) +} + +func testAccQueueRedriveAllowPolicyConfig_updated(rName string) string { + return fmt.Sprintf(` +resource "aws_sqs_queue" "test" { + name = %[1]q +} + +resource "aws_sqs_queue" "test_src" { + name = "%[1]s_src" + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.test.arn + maxReceiveCount = 4 + }) +} + +resource "aws_sqs_queue_redrive_allow_policy" "test" { + queue_url = aws_sqs_queue.test.id + redrive_allow_policy = jsonencode({ + redrivePermission = "allowAll" + }) +} +`, rName) +} diff --git a/internal/service/sqs/queue_redrive_policy_test.go b/internal/service/sqs/queue_redrive_policy_test.go index 0d7c6907423..649e8f975c6 100644 --- a/internal/service/sqs/queue_redrive_policy_test.go +++ b/internal/service/sqs/queue_redrive_policy_test.go @@ -24,7 +24,7 @@ func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedrivePolicyConfig_basic(rName), + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), @@ -37,7 +37,7 @@ func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccQueueRedrivePolicyConfig_basic(rName), + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), PlanOnly: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "redrive_policy", queueResourceName, "redrive_policy"), @@ -60,7 +60,7 @@ func TestAccSQSQueueRedrivePolicy_disappears(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedrivePolicyConfig_basic(rName), + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), @@ -84,7 +84,7 @@ func TestAccSQSQueueRedrivePolicy_Disappears_queue(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedrivePolicyConfig_basic(rName), + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueue(), queueResourceName), @@ -108,7 +108,7 @@ func TestAccSQSQueueRedrivePolicy_update(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedrivePolicyConfig_basic(rName), + Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), @@ -120,7 +120,7 @@ func TestAccSQSQueueRedrivePolicy_update(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccQueueRedrivePolicyConfig_updated(rName), + Config: testAccQueueRedriveAllowPolicyConfig_updated(rName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), ), diff --git a/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown b/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown new file mode 100644 index 00000000000..ad58a888876 --- /dev/null +++ b/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown @@ -0,0 +1,56 @@ +--- +subcategory: "SQS (Simple Queue)" +layout: "aws" +page_title: "AWS: aws_sqs_queue_redrive_allow_policy" +description: |- +Provides a SQS Queue Redrive Allow Policy resource. +--- + +# Resource: aws_sqs_queue_redrive_allow_policy + +Provides a SQS Queue Redrive Allow Policy resource. + +## Example Usage + +```terraform +resource "aws_sqs_queue" "src" { + name = "srcqueue" + + redrive_policy = jsonencode({ + deadLetterTargetArn = aws_sqs_queue.example.arn + maxReceiveCount = 4 + }) +} + +resource "aws_sqs_queue" "example" { + name = "examplequeue" +} + +resource "aws_sqs_queue_redrive_allow_policy" "example" { + queue_url = aws_sqs_queue.example.id + + redrive_allow_policy = jsonencode({ + redrivePermission = "byQueue", + sourceQueueArns = [aws_sqs_queue.src.arn] + }) +} +``` + +## Argument Reference + +The following arguments are supported: + +* `queue_url` - (Required) The URL of the SQS Queue to which to attach the policy +* `redrive_allow_policy` - (Required) The JSON redrive allow policy for the SQS queue. Learn more in the [Amazon SQS dead-letter queues documentation](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html). + +## Attributes Reference + +No additional attributes are exported. + +## Import + +SQS Queue Redrive Allow Policies can be imported using the queue URL, e.g., + +``` +$ terraform import aws_sqs_queue_redrive_allow_policy.test https://queue.amazonaws.com/0123456789012/myqueue +``` From 416d0a14290178514d57520cdaa6928c997c384a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 10:45:05 -0400 Subject: [PATCH 13/14] Fix typos. --- internal/service/sqs/queue_redrive_policy_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/sqs/queue_redrive_policy_test.go b/internal/service/sqs/queue_redrive_policy_test.go index 649e8f975c6..0d7c6907423 100644 --- a/internal/service/sqs/queue_redrive_policy_test.go +++ b/internal/service/sqs/queue_redrive_policy_test.go @@ -24,7 +24,7 @@ func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), @@ -37,7 +37,7 @@ func TestAccSQSQueueRedrivePolicy_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Config: testAccQueueRedrivePolicyConfig_basic(rName), PlanOnly: true, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "redrive_policy", queueResourceName, "redrive_policy"), @@ -60,7 +60,7 @@ func TestAccSQSQueueRedrivePolicy_disappears(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), testAccCheckQueueExists(fmt.Sprintf("%s_ddl", queueResourceName), &queueAttributes), @@ -84,7 +84,7 @@ func TestAccSQSQueueRedrivePolicy_Disappears_queue(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), acctest.CheckResourceDisappears(acctest.Provider, tfsqs.ResourceQueue(), queueResourceName), @@ -108,7 +108,7 @@ func TestAccSQSQueueRedrivePolicy_update(t *testing.T) { CheckDestroy: testAccCheckQueueDestroy, Steps: []resource.TestStep{ { - Config: testAccQueueRedriveAllowPolicyConfig_basic(rName), + Config: testAccQueueRedrivePolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckQueueExists(queueResourceName, &queueAttributes), resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), @@ -120,7 +120,7 @@ func TestAccSQSQueueRedrivePolicy_update(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccQueueRedriveAllowPolicyConfig_updated(rName), + Config: testAccQueueRedrivePolicyConfig_updated(rName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(resourceName, "redrive_policy"), ), From e92c05848c8335d13f482d65c6073199270b5893 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 13 Sep 2022 10:53:53 -0400 Subject: [PATCH 14/14] Fix "error checking file frontmatter: error parsing YAML frontmatter: yaml: line 7: could not find expected ':'". --- website/docs/r/sqs_queue_redrive_allow_policy.html.markdown | 2 +- website/docs/r/sqs_queue_redrive_policy.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown b/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown index ad58a888876..c166f7a52eb 100644 --- a/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown +++ b/website/docs/r/sqs_queue_redrive_allow_policy.html.markdown @@ -3,7 +3,7 @@ subcategory: "SQS (Simple Queue)" layout: "aws" page_title: "AWS: aws_sqs_queue_redrive_allow_policy" description: |- -Provides a SQS Queue Redrive Allow Policy resource. + Provides a SQS Queue Redrive Allow Policy resource. --- # Resource: aws_sqs_queue_redrive_allow_policy diff --git a/website/docs/r/sqs_queue_redrive_policy.html.markdown b/website/docs/r/sqs_queue_redrive_policy.html.markdown index c3204e4ac88..e274007b7f9 100644 --- a/website/docs/r/sqs_queue_redrive_policy.html.markdown +++ b/website/docs/r/sqs_queue_redrive_policy.html.markdown @@ -3,7 +3,7 @@ subcategory: "SQS (Simple Queue)" layout: "aws" page_title: "AWS: aws_sqs_queue_redrive_policy" description: |- -Provides a SQS Queue Redrive Policy resource. + Provides a SQS Queue Redrive Policy resource. --- # Resource: aws_sqs_queue_redrive_policy