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

add override_provider block to s3 object copy #40689

Merged
Merged
Changes from 1 commit
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
Next Next commit
add ignore_default_tags option to s3 object copy
  • Loading branch information
Jacques Fournier committed Dec 24, 2024

Verified

This commit was signed with the committer’s verified signature. The key has expired.
aramase Anish Ramasekar
commit 6ee661ea20208b1fd1e2f196b85f59ff47efd1b1
42 changes: 38 additions & 4 deletions internal/service/s3/object_copy.go
Original file line number Diff line number Diff line change
@@ -40,6 +40,13 @@ func resourceObjectCopy() *schema.Resource {
UpdateWithoutTimeout: resourceObjectCopyUpdate,
DeleteWithoutTimeout: resourceObjectCopyDelete,

CustomizeDiff: func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
if ignoreProviderDefaultTags(ctx, d) {
return d.SetNew(names.AttrTagsAll, d.Get(names.AttrTags))
}
return verify.SetTagsDiff(ctx, d, meta)
},

Schema: map[string]*schema.Schema{
"acl": {
Type: schema.TypeString,
@@ -263,6 +270,30 @@ func resourceObjectCopy() *schema.Resource {
Computed: true,
ValidateFunc: validation.IsRFC3339Time,
},
"override_provider": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"default_tags": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"tags": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ValidateDiagFunc: verify.MapSizeBetween(0, 0),
},
},
},
},
},
},
},
"request_charged": {
Type: schema.TypeBool,
Computed: true,
@@ -324,8 +355,6 @@ func resourceObjectCopy() *schema.Resource {
Computed: true,
},
},

CustomizeDiff: verify.SetTagsDiff,
}
}

@@ -649,8 +678,13 @@ func resourceObjectCopyDoCopy(ctx context.Context, d *schema.ResourceData, meta
}

defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig(ctx)
tags := tftags.New(ctx, getContextTags(ctx))
tags = defaultTagsConfig.MergeTags(tags)
tags := tftags.New(ctx, d.Get("tags").(map[string]interface{}))
if ignoreProviderDefaultTags(ctx, d) {
tags = tags.RemoveDefaultConfig(defaultTagsConfig)
} else {
tags = defaultTagsConfig.MergeTags(tftags.New(ctx, tags))
}

if len(tags) > 0 {
// The tag-set must be encoded as URL Query parameters.
input.Tagging = aws.String(tags.IgnoreAWS().URLEncode())
227 changes: 227 additions & 0 deletions internal/service/s3/object_copy_test.go
Original file line number Diff line number Diff line change
@@ -218,6 +218,143 @@ func TestAccS3ObjectCopy_BucketKeyEnabled_object(t *testing.T) {
})
}

func TestAccS3ObjectCopy_DefaultTags_providerOnly(t *testing.T) {
ctx := acctest.Context(t)
rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_object_copy.test"
sourceKey := "dir1/dir2/source"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectCopyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1(acctest.CtProviderKey1, acctest.CtProviderValue1),
testAccObjectCopyConfig_simple(rName1, sourceKey, rName2, names.AttrTarget),
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectCopyExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"),
resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"),
),
},
},
})
}

func TestAccS3ObjectCopy_DefaultTags_providerAndResource(t *testing.T) {
ctx := acctest.Context(t)
rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_object_copy.test"
sourceKey := "dir1/dir2/source"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectCopyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1(acctest.CtProviderKey1, acctest.CtProviderValue1),
testAccObjectCopyConfig_tags(rName1, sourceKey, rName2, names.AttrTarget),
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectCopyExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
resource.TestCheckResourceAttr(resourceName, "tags.Key1", "A@AA"),
resource.TestCheckResourceAttr(resourceName, "tags.Key2", "BBB"),
resource.TestCheckResourceAttr(resourceName, "tags.Key3", "CCC"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "4"),
resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key1", "A@AA"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key2", "BBB"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key3", "CCC"),
),
},
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1(acctest.CtProviderKey1, acctest.CtProviderValue1),
testAccObjectCopyConfig_updatedTags(rName1, sourceKey, rName2, names.AttrTarget),
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectCopyExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "4"),
resource.TestCheckResourceAttr(resourceName, "tags.Key2", "B@BB"),
resource.TestCheckResourceAttr(resourceName, "tags.Key3", "X X"),
resource.TestCheckResourceAttr(resourceName, "tags.Key4", "DDD"),
resource.TestCheckResourceAttr(resourceName, "tags.Key5", "E:/"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "5"),
resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key2", "B@BB"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key3", "X X"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key4", "DDD"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key5", "E:/"),
),
},
},
})
}

func TestAccS3ObjectCopy_DefaultTags_providerAndResourceWithOverride(t *testing.T) {
ctx := acctest.Context(t)
rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_object_copy.test"
sourceKey := "dir1/dir2/source"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectCopyDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1(acctest.CtProviderKey1, acctest.CtProviderValue1),
testAccObjectCopyConfig_tagsWithOverride(rName1, sourceKey, rName2, names.AttrTarget),
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectCopyExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "3"),
resource.TestCheckResourceAttr(resourceName, "tags.Key1", "A@AA"),
resource.TestCheckResourceAttr(resourceName, "tags.Key2", "BBB"),
resource.TestCheckResourceAttr(resourceName, "tags.Key3", "CCC"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "3"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key1", "A@AA"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key2", "BBB"),
resource.TestCheckResourceAttr(resourceName, "tags_all.Key3", "CCC"),
),
},
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1(acctest.CtProviderKey1, acctest.CtProviderValue1),
testAccObjectCopyConfig_updatedTagsOverride(rName1, sourceKey, rName2, names.AttrTarget),
),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectCopyExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "tags.%", "4"),
resource.TestCheckResourceAttr(resourceName, "tags.Key2", "B@BB"),
resource.TestCheckResourceAttr(resourceName, "tags.Key3", "X X"),
resource.TestCheckResourceAttr(resourceName, "tags.Key4", "DDD"),
resource.TestCheckResourceAttr(resourceName, "tags.Key5", "E:/"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "4"),
resource.TestCheckResourceAttr(resourceName, "tags.Key2", "B@BB"),
resource.TestCheckResourceAttr(resourceName, "tags.Key3", "X X"),
resource.TestCheckResourceAttr(resourceName, "tags.Key4", "DDD"),
resource.TestCheckResourceAttr(resourceName, "tags.Key5", "E:/"),
),
},
},
})
}

func TestAccS3ObjectCopy_sourceWithSlashes(t *testing.T) {
ctx := acctest.Context(t)
rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
@@ -709,6 +846,96 @@ resource "aws_s3_object_copy" "test" {
`, targetKey))
}

func testAccObjectCopyConfig_tags(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceObject(sourceBucket, sourceKey, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
bucket = aws_s3_bucket.target.bucket
key = %[1]q
source = "${aws_s3_bucket.source.bucket}/${aws_s3_object.source.key}"
tags = {
Key1 = "A@AA"
Key2 = "BBB"
Key3 = "CCC"
}
tagging_directive="REPLACE"

}
`, targetKey))
}

func testAccObjectCopyConfig_tagsWithOverride(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceObject(sourceBucket, sourceKey, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
bucket = aws_s3_bucket.target.bucket
key = %[1]q
source = "${aws_s3_bucket.source.bucket}/${aws_s3_object.source.key}"
tags = {
Key1 = "A@AA"
Key2 = "BBB"
Key3 = "CCC"
}
tagging_directive="REPLACE"
override_provider {
default_tags {
tags = {}
}
}

}
`, targetKey))
}

func testAccObjectCopyConfig_updatedTags(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceObject(sourceBucket, sourceKey, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
bucket = aws_s3_bucket.target.bucket
key = %[1]q
source = "${aws_s3_bucket.source.bucket}/${aws_s3_object.source.key}"
tags = {
Key2 = "B@BB"
Key3 = "X X"
Key4 = "DDD"
Key5 = "E:/"
}
tagging_directive="REPLACE"

}
`, targetKey))
}

func testAccObjectCopyConfig_updatedTagsOverride(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceObject(sourceBucket, sourceKey, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
bucket = aws_s3_bucket.target.bucket
key = %[1]q
source = "${aws_s3_bucket.source.bucket}/${aws_s3_object.source.key}"
tags = {
Key2 = "B@BB"
Key3 = "X X"
Key4 = "DDD"
Key5 = "E:/"
}
tagging_directive="REPLACE"
override_provider {
default_tags {
tags = {}
}
}
}
`, targetKey))
}

func testAccObjectCopyConfig_simple(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceObject(sourceBucket, sourceKey, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
bucket = aws_s3_bucket.target.bucket
key = %[1]q
source = "${aws_s3_bucket.source.bucket}/${aws_s3_object.source.key}"

}
`, targetKey))
}

func testAccObjectCopyConfig_externalSourceObject(sourceBucket, sourceKey, targetBucket, targetKey string) string {
return acctest.ConfigCompose(testAccObjectCopyConfig_baseSourceAndTargetBuckets(sourceBucket, targetBucket), fmt.Sprintf(`
resource "aws_s3_object_copy" "test" {
25 changes: 25 additions & 0 deletions website/docs/r/s3_object_copy.html.markdown
Original file line number Diff line number Diff line change
@@ -26,6 +26,25 @@ resource "aws_s3_object_copy" "test" {
}
```

### Ignoring Provider `default_tags`

S3 objects support a [maximum of 10 tags](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-tagging.html).
If the resource's own `tags` and the provider-level `default_tags` would together lead to more than 10 tags on an S3 object copy, use the `override_provider` configuration block to suppress any provider-level `default_tags`.

```terraform
resource "aws_s3_object_copy" "test" {
bucket = "destination_bucket"
key = "destination_key"
source = "source_bucket/source_key"
override_provider {
default_tags {
tags = {}
}
}

}
```

## Argument Reference

The following arguments are required:
@@ -89,6 +108,12 @@ This configuration block has the following optional arguments (one of the three

-> **Note:** Terraform ignores all leading `/`s in the object's `key` and treats multiple `/`s in the rest of the object's `key` as a single `/`, so values of `/index.html` and `index.html` correspond to the same S3 object as do `first//second///third//` and `first/second/third/`.

### Override Provider

The `override_provider` block supports the following:

* `default_tags` - (Optional) Override the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block).

## Attribute Reference

This resource exports the following attributes in addition to the arguments above: