Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/34787.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
provider: Add `sqs_wait_times` configuration block to customize SQS queue operation wait times for local development environments
```
66 changes: 66 additions & 0 deletions docs/retries-and-waiters.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,69 @@ func waitThingDeleted(ctx context.Context, conn *example.Example, id string, tim
```

Typically, the AWS Go SDK should include constants for various status field values (e.g., `StatusCreating` for `CREATING`). If not, create them in a file named `internal/service/{SERVICE}/consts.go`.

## Configurable Wait Times

Some AWS services require configurable wait times to accommodate different environments and use cases. For example, SQS queue operations have different propagation characteristics in local development environments (like LocalStack) compared to production AWS environments.

### SQS Wait Times Configuration

The SQS service supports configurable wait times through the provider configuration to optimize performance for different environments:

```hcl
provider "aws" {
# ... other provider configuration ...

sqs_wait_times {
create_continuous_target_occurrence = 1 # Default: 6 (30 seconds)
delete_continuous_target_occurrence = 2 # Default: 15 (45 seconds)
}
}
```

#### Configuration Options

- **`create_continuous_target_occurrence`** (Optional, Number): Number of consecutive successful checks required during SQS queue creation. Default is 6, which results in approximately 30 seconds of wait time (6 checks × 5 seconds). Lower values speed up local development environments.

- **`delete_continuous_target_occurrence`** (Optional, Number): Number of consecutive successful checks required during SQS queue deletion. Default is 15, which results in approximately 45 seconds of wait time (15 checks × 3 seconds). Lower values speed up local development environments.

#### Use Cases

**Local Development (LocalStack):**
```hcl
provider "aws" {
# LocalStack configuration
access_key = "test"
secret_key = "test"
region = "us-east-1"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
skip_requesting_account_id = true

# Fast wait times for local development
sqs_wait_times {
create_continuous_target_occurrence = 1 # ~5 seconds
delete_continuous_target_occurrence = 2 # ~6 seconds
}

endpoints {
sqs = "http://localhost:4566"
}
}
```

**Production AWS:**
```hcl
provider "aws" {
# Production configuration uses default wait times
# (no sqs_wait_times block needed)
}
```

#### Implementation Notes

- Default values are designed to accommodate various AWS regions including GovCloud, commercial, and China regions
- Lower values should only be used in controlled environments where eventual consistency is not a concern
- The configuration affects both `aws_sqs_queue` resource creation and deletion operations
- Changes to wait times do not affect existing resources, only new operations
14 changes: 10 additions & 4 deletions internal/conns/awsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ type AWSClient struct {
partition endpoints.Partition
servicePackages map[string]ServicePackage
s3ExpressClient *s3.Client
s3UsePathStyle bool // From provider configuration.
s3USEast1RegionalEndpoint string // From provider configuration.
stsRegion string // From provider configuration.
terraformVersion string // From provider configuration.
s3UsePathStyle bool // From provider configuration.
s3USEast1RegionalEndpoint string // From provider configuration.
sqsWaitTimes *SQSWaitTimesConfig // From provider configuration.
stsRegion string // From provider configuration.
terraformVersion string // From provider configuration.
}

func (c *AWSClient) SetServicePackages(_ context.Context, servicePackages map[string]ServicePackage) {
Expand Down Expand Up @@ -412,3 +413,8 @@ func client[T any](ctx context.Context, c *AWSClient, servicePackageName string,

return client, nil
}

// SQSWaitTimes returns the SQS wait times configuration
func (c *AWSClient) SQSWaitTimes() *SQSWaitTimesConfig {
return c.sqsWaitTimes
}
7 changes: 7 additions & 0 deletions internal/conns/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type Config struct {
TokenBucketRateLimiterCapacity int
UseDualStackEndpoint bool
UseFIPSEndpoint bool
SQSWaitTimes *SQSWaitTimesConfig
}

type SQSWaitTimesConfig struct {
CreateContinuousTargetOccurrence int
DeleteContinuousTargetOccurrence int
}

// ConfigureProvider configures the provided provider Meta (instance data).
Expand Down Expand Up @@ -203,6 +209,7 @@ func (c *Config) ConfigureProvider(ctx context.Context, client *AWSClient) (*AWS
client.logger = logger
client.s3UsePathStyle = c.S3UsePathStyle
client.s3USEast1RegionalEndpoint = c.S3USEast1RegionalEndpoint
client.sqsWaitTimes = c.SQSWaitTimes
client.stsRegion = c.STSRegion

return client, diags
Expand Down
32 changes: 32 additions & 0 deletions internal/provider/sdkv2/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,30 @@ func NewProvider(ctx context.Context) (*schema.Provider, error) {
Description: "Skip requesting the account ID. " +
"Used for AWS API implementations that do not have IAM/STS API and/or metadata API.",
},
"sqs_wait_times": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Configuration block for SQS wait times. Useful for local development environments like LocalStack.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"create_continuous_target_occurrence": {
Type: schema.TypeInt,
Optional: true,
Default: 6,
ValidateFunc: validation.IntAtLeast(1),
Description: "Number of consecutive successful checks required during SQS queue creation. Default is 6 (30 seconds). Lower values speed up local development.",
},
"delete_continuous_target_occurrence": {
Type: schema.TypeInt,
Optional: true,
Default: 15,
ValidateFunc: validation.IntAtLeast(1),
Description: "Number of consecutive successful checks required during SQS queue deletion. Default is 15 (45 seconds). Lower values speed up local development.",
},
},
},
},
"sts_region": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -485,6 +509,14 @@ func (p *sdkProvider) configure(ctx context.Context, d *schema.ResourceData) (an
}
}

if v, ok := d.GetOk("sqs_wait_times"); ok && len(v.([]any)) > 0 && v.([]any)[0] != nil {
sqsWaitTimes := v.([]any)[0].(map[string]any)
config.SQSWaitTimes = &conns.SQSWaitTimesConfig{
CreateContinuousTargetOccurrence: sqsWaitTimes["create_continuous_target_occurrence"].(int),
DeleteContinuousTargetOccurrence: sqsWaitTimes["delete_continuous_target_occurrence"].(int),
}
}

var c *conns.AWSClient
if v, ok := p.provider.Meta().(*conns.AWSClient); ok {
c = v
Expand Down
4 changes: 2 additions & 2 deletions internal/service/sqs/attribute_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (h *queueAttributeHandler) Upsert(ctx context.Context, d *schema.ResourceDa

d.SetId(url)

if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, deadline.Remaining()); err != nil {
if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, deadline.Remaining(), meta); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SQS Queue (%s) attribute (%s) create: %s", d.Id(), h.AttributeName, err)
}

Expand Down Expand Up @@ -123,7 +123,7 @@ func (h *queueAttributeHandler) Delete(ctx context.Context, d *schema.ResourceDa
return sdkdiag.AppendErrorf(diags, "deleting SQS Queue (%s) attribute (%s): %s", d.Id(), h.AttributeName, err)
}

if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, d.Timeout(schema.TimeoutDelete)); err != nil {
if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, d.Timeout(schema.TimeoutDelete), meta); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SQS Queue (%s) attribute (%s) delete: %s", d.Id(), h.AttributeName, err)
}

Expand Down
24 changes: 17 additions & 7 deletions internal/service/sqs/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func resourceQueueCreate(ctx context.Context, d *schema.ResourceData, meta any)

d.SetId(aws.ToString(outputRaw.(*sqs.CreateQueueOutput).QueueUrl))

if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, deadline.Remaining()); err != nil {
if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, deadline.Remaining(), meta); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SQS Queue (%s) attributes create: %s", d.Id(), err)
}

Expand Down Expand Up @@ -342,7 +342,7 @@ func resourceQueueUpdate(ctx context.Context, d *schema.ResourceData, meta any)
return sdkdiag.AppendErrorf(diags, "updating SQS Queue (%s) attributes: %s", d.Id(), err)
}

if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, d.Timeout(schema.TimeoutUpdate)); err != nil {
if err := waitQueueAttributesPropagated(ctx, conn, d.Id(), attributes, d.Timeout(schema.TimeoutUpdate), meta); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SQS Queue (%s) attributes update: %s", d.Id(), err)
}
}
Expand All @@ -367,7 +367,7 @@ func resourceQueueDelete(ctx context.Context, d *schema.ResourceData, meta any)
return sdkdiag.AppendErrorf(diags, "deleting SQS Queue (%s): %s", d.Id(), err)
}

if err := waitQueueDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil {
if err := waitQueueDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete), meta); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SQS Queue (%s) delete: %s", d.Id(), err)
}

Expand Down Expand Up @@ -600,13 +600,18 @@ func statusQueueAttributeState(ctx context.Context, conn *sqs.Client, url string
}
}

func waitQueueAttributesPropagated(ctx context.Context, conn *sqs.Client, url string, expected map[types.QueueAttributeName]string, timeout time.Duration) error {
func waitQueueAttributesPropagated(ctx context.Context, conn *sqs.Client, url string, expected map[types.QueueAttributeName]string, timeout time.Duration, meta any) error {
continuousTargetOccurrence := 6
if awsClient, ok := meta.(*conns.AWSClient); ok && awsClient.SQSWaitTimes() != nil {
continuousTargetOccurrence = awsClient.SQSWaitTimes().CreateContinuousTargetOccurrence
}

stateConf := &retry.StateChangeConf{
Pending: []string{queueAttributeStateNotEqual},
Target: []string{queueAttributeStateEqual},
Refresh: statusQueueAttributeState(ctx, conn, url, expected),
Timeout: timeout,
ContinuousTargetOccurence: 6, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
ContinuousTargetOccurence: continuousTargetOccurrence,
MinTimeout: 5 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
NotFoundChecks: 10, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
}
Expand All @@ -616,13 +621,18 @@ func waitQueueAttributesPropagated(ctx context.Context, conn *sqs.Client, url st
return err
}

func waitQueueDeleted(ctx context.Context, conn *sqs.Client, url string, timeout time.Duration) error {
func waitQueueDeleted(ctx context.Context, conn *sqs.Client, url string, timeout time.Duration, meta any) error {
continuousTargetOccurrence := 15
if awsClient, ok := meta.(*conns.AWSClient); ok && awsClient.SQSWaitTimes() != nil {
continuousTargetOccurrence = awsClient.SQSWaitTimes().DeleteContinuousTargetOccurrence
}

stateConf := &retry.StateChangeConf{
Pending: []string{queueStateExists},
Target: []string{},
Refresh: statusQueueState(ctx, conn, url),
Timeout: timeout,
ContinuousTargetOccurence: 15, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
ContinuousTargetOccurence: continuousTargetOccurrence,
MinTimeout: 3 * time.Second, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
NotFoundChecks: 5, // set to accommodate GovCloud, commercial, China, etc. - avoid lowering
}
Expand Down
Loading