Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/s3_bucket_lifecycle_configuration: correctly configure filter tag and add and block documentation #23252

Merged
merged 6 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/23252.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_s3_bucket_lifecycle_configuration: Ensure both `key` and `value` arguments of the `filter` `tag` configuration block are correctly populated in the outgoing API request and terraform state.
```
69 changes: 69 additions & 0 deletions internal/service/s3/bucket_lifecycle_configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,43 @@ func TestAccS3BucketLifecycleConfiguration_prefix(t *testing.T) {
})
}

// Reference: https://github.com/hashicorp/terraform-provider-aws/issues/23239
func TestAccS3BucketLifecycleConfiguration_Filter_Tag(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_bucket_lifecycle_configuration.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, s3.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckBucketLifecycleConfigurationDestroy,
Steps: []resource.TestStep{
{
Config: testAccBucketLifecycleConfiguration_Filter_TagConfig(rName, "key1", "value1"),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketLifecycleConfigurationExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "rule.#", "1"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule.*", map[string]string{
"expiration.#": "1",
"expiration.0.days": "365",
"id": rName,
"filter.#": "1",
"filter.0.tag.#": "1",
"filter.0.tag.0.key": "key1",
"filter.0.tag.0.value": "value1",
"status": tfs3.LifecycleRuleStatusEnabled,
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccS3BucketLifecycleConfiguration_RuleExpiration_expireMarkerOnly(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_bucket_lifecycle_configuration.test"
Expand Down Expand Up @@ -794,6 +831,38 @@ resource "aws_s3_bucket_lifecycle_configuration" "test" {
`, rName, prefix)
}

func testAccBucketLifecycleConfiguration_Filter_TagConfig(rName, key, value string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
bucket = %[1]q
}

resource "aws_s3_bucket_acl" "test" {
bucket = aws_s3_bucket.test.id
acl = "private"
}

resource "aws_s3_bucket_lifecycle_configuration" "test" {
bucket = aws_s3_bucket.test.bucket
rule {
id = %[1]q
status = "Enabled"

filter {
tag {
key = %[2]q
value = %[3]q
}
}

expiration {
days = 365
}
}
}
`, rName, key, value)
}

func testAccBucketLifecycleConfiguration_RuleExpiration_expiredDeleteMarkerConfig(rName string, expired bool) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
Expand Down
35 changes: 29 additions & 6 deletions internal/service/s3/flex.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,7 @@ func ExpandLifecycleRuleFilter(l []interface{}) *s3.LifecycleRuleFilter {
}

if v, ok := m["tag"].([]interface{}); ok && len(v) > 0 && v[0] != nil {
tags := Tags(tftags.New(v[0]).IgnoreAWS())
if len(tags) > 0 {
result.Tag = tags[0]
}
result.Tag = ExpandLifecycleRuleFilterTag(v[0].(map[string]interface{}))
}

if v, ok := m["prefix"].(string); ok && result.And == nil && result.Tag == nil {
Expand Down Expand Up @@ -281,6 +278,24 @@ func ExpandLifecycleRuleFilterAndOperator(m map[string]interface{}) *s3.Lifecycl
return result
}

func ExpandLifecycleRuleFilterTag(m map[string]interface{}) *s3.Tag {
if len(m) == 0 {
return nil
}

result := &s3.Tag{}

if key, ok := m["key"].(string); ok {
result.Key = aws.String(key)
}

if value, ok := m["value"].(string); ok {
result.Value = aws.String(value)
}

return result
}

func ExpandLifecycleRuleNoncurrentVersionExpiration(m map[string]interface{}) *s3.NoncurrentVersionExpiration {
if len(m) == 0 {
return nil
Expand Down Expand Up @@ -950,9 +965,17 @@ func FlattenLifecycleRuleFilterTag(tag *s3.Tag) []interface{} {
return nil
}

t := KeyValueTags([]*s3.Tag{tag}).IgnoreAWS().Map()
m := make(map[string]interface{})

return []interface{}{t}
if tag.Key != nil {
m["key"] = aws.StringValue(tag.Key)
}

if tag.Value != nil {
m["value"] = aws.StringValue(tag.Value)
}

return []interface{}{m}
}

func FlattenLifecycleRuleNoncurrentVersionExpiration(expiration *s3.NoncurrentVersionExpiration) []interface{} {
Expand Down
29 changes: 10 additions & 19 deletions website/docs/r/s3_bucket_lifecycle_configuration.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -119,24 +119,6 @@ resource "aws_s3_bucket_lifecycle_configuration" "versioning-bucket-config" {
}
```

## Usage Notes

~> **NOTE:** To avoid conflicts always add the following lifecycle object to the `aws_s3_bucket` resource of the source bucket.

This resource implements the same features that are provided by the `lifecycle_rule` object of the [`aws_s3_bucket` resource](s3_bucket.html). To avoid conflicts or unexpected apply results, a lifecycle configuration is needed on the `aws_s3_bucket` to ignore changes to the internal `lifecycle_rule` object. Failure to add the `lifecycle` configuration to the `aws_s3_bucket` will result in conflicting state results.

```
lifecycle {
ignore_changes = [
lifecycle_rule
]
}
```

The `aws_s3_bucket_lifecycle_configuration` resource provides the following features that are not available in the [`aws_s3_bucket` resource](s3_bucket.html):

* `filter` - Added to the `rule` configuration block [documented below](#filter).

## Argument Reference

The following arguments are supported:
Expand Down Expand Up @@ -177,7 +159,7 @@ The `expiration` configuration block supports the following arguments:

The `filter` configuration block supports the following arguments:

* `and`- (Optional) Configuration block used to apply a logical `AND` to two or more predicates. The Lifecycle Rule will apply to any object matching all of the predicates configured inside the `and` block.
* `and`- (Optional) Configuration block used to apply a logical `AND` to two or more predicates [documented below](#and). The Lifecycle Rule will apply to any object matching all of the predicates configured inside the `and` block.
* `object_size_greater_than` - (Optional) Minimum object size to which the rule applies.
* `object_size_less_than` - (Optional) Maximum object size to which the rule applies.
* `prefix` - (Optional) Prefix identifying one or more objects to which the rule applies.
Expand Down Expand Up @@ -208,6 +190,15 @@ The `transition` configuration block supports the following arguments:
* `days` - (Optional, Conflicts with `date`) The number of days after creation when objects are transitioned to the specified storage class. The value must be a positive integer. If both `days` and `date` are not specified, defaults to `0`. Valid values depend on `storage_class`, see [Transition objects using Amazon S3 Lifecycle](https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-transition-general-considerations.html) for more details.
* `storage_class` - The class of storage used to store the object. Valid Values: `GLACIER`, `STANDARD_IA`, `ONEZONE_IA`, `INTELLIGENT_TIERING`, `DEEP_ARCHIVE`, `GLACIER_IR`.

### and
Copy link
Member

Choose a reason for hiding this comment

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

I like the upgrade guide having filter examples using and and just prefix.


The `and` configuration block supports the following arguments:

* `object_size_greater_than` - (Optional) Minimum object size to which the rule applies. Value must be at least `0` if specified.
* `object_size_less_than` - (Optional) Maximum object size to which the rule applies. Value must be at least `1` if specified.
* `prefix` - (Optional) Prefix identifying one or more objects to which the rule applies.
* `tags` - (Optional) Key-value map of resource tags. All of these tags must exist in the object's tag set in order for the rule to apply.

### tag

The `tag` configuration block supports the following arguments:
Expand Down