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 ignore_default_tags option to s3 object #33262

Merged
merged 25 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f789c4c
add ignore_default_tags option to s3 object
walteh Aug 31, 2023
4ef762a
changelog and lint
walteh Aug 31, 2023
56221d5
bucket tag tests for object ignore_default_tags
walteh Sep 1, 2023
3cf0ae1
format test terraform config
walteh Sep 1, 2023
bf7df57
add ignore_default_tags to web docs
walteh Sep 1, 2023
8f0fb27
Revert "add ignore_default_tags to web docs"
ewbankkit Oct 31, 2023
9f1e2d1
Revert "format test terraform config"
ewbankkit Oct 31, 2023
44d1cb3
Revert "bucket tag tests for object ignore_default_tags"
ewbankkit Oct 31, 2023
ecbbf85
Revert "changelog and lint"
ewbankkit Oct 31, 2023
5945bd0
Revert "add ignore_default_tags option to s3 object"
ewbankkit Oct 31, 2023
a973e68
Merge branch 'main' into HEAD
ewbankkit Oct 31, 2023
c60a671
Add 'TestAccS3Object_DefaultTags_providerOnly'.
ewbankkit Oct 31, 2023
c4ee5d9
r/aws_s3_object: Add 'override_provider' configuration block.
ewbankkit Oct 31, 2023
3db0fdc
Add 'verify.MapLenBetween'.
ewbankkit Oct 31, 2023
891211d
Add 'GetOk' to 'verify.ResourceDiffer'.
ewbankkit Oct 31, 2023
889db5f
r/aws_s3_object: Add capability to ignore provider 'default_tags'.
ewbankkit Oct 31, 2023
24e0c28
Add CHANGELOG entry.
ewbankkit Oct 31, 2023
5f6d798
Acceptance test output:
ewbankkit Oct 31, 2023
0ceebc5
Add 'TestAccS3Object_DefaultTags_providerAndResource'.
ewbankkit Oct 31, 2023
38999c3
Acceptance test output:
ewbankkit Oct 31, 2023
b86a61f
r/aws_s3_object: Tweak 'expandOverrideProviderModel'.
ewbankkit Oct 31, 2023
3711d9b
Add 'TestAccS3Object_DefaultTags_providerAndResourceWithOverride'.
ewbankkit Oct 31, 2023
5dc6899
Acceptance test output:
ewbankkit Oct 31, 2023
471acb0
r/aws_s3_object: Document 'override_provider'.
ewbankkit Oct 31, 2023
958142f
Correct CHANGELOG entry file name.
ewbankkit Oct 31, 2023
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/33262.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_s3_object: Add `override_provider` configuration block, allowing tags inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) to be ignored
```
86 changes: 84 additions & 2 deletions internal/service/s3/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ func ResourceObject() *schema.Resource {

CustomizeDiff: customdiff.Sequence(
resourceObjectCustomizeDiff,
verify.SetTagsDiff,
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
if ignoreProviderDefaultTags(ctx, d) {
return d.SetNew("tags_all", d.Get("tags"))
}
return verify.SetTagsDiff(ctx, d, meta)
},
),

Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -180,6 +185,30 @@ func ResourceObject() *schema.Resource {
Optional: 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.MapLenBetween(0, 0),
},
},
},
},
},
},
},
"server_side_encryption": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -403,7 +432,13 @@ func resourceObjectUpload(ctx context.Context, d *schema.ResourceData, meta inte
conn := meta.(*conns.AWSClient).S3Client(ctx)
uploader := manager.NewUploader(conn)
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(ctx, d.Get("tags").(map[string]interface{})))
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))
}

var body io.ReadSeeker

Expand Down Expand Up @@ -669,3 +704,50 @@ func sdkv1CompatibleCleanKey(key string) string {
key = regexache.MustCompile(`/+`).ReplaceAllString(key, "/")
return key
}

func ignoreProviderDefaultTags(ctx context.Context, d verify.ResourceDiffer) bool {
if v, ok := d.GetOk("override_provider"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
if data := expandOverrideProviderModel(ctx, v.([]interface{})[0].(map[string]interface{})); data != nil && data.DefaultTagsConfig != nil {
return len(data.DefaultTagsConfig.Tags) == 0
}
}

return false
}

type overrideProviderModel struct {
DefaultTagsConfig *tftags.DefaultConfig
}

func expandOverrideProviderModel(ctx context.Context, tfMap map[string]interface{}) *overrideProviderModel {
if tfMap == nil {
return nil
}

data := &overrideProviderModel{}

if v, ok := tfMap["default_tags"].([]interface{}); ok && len(v) > 0 {
if v[0] != nil {
data.DefaultTagsConfig = expandDefaultTags(ctx, v[0].(map[string]interface{}))
} else {
// Ensure that DefaultTagsConfig is not nil as it's checked in ignoreProviderDefaultTags.
data.DefaultTagsConfig = expandDefaultTags(ctx, map[string]interface{}{})
}
}

return data
}

func expandDefaultTags(ctx context.Context, tfMap map[string]interface{}) *tftags.DefaultConfig {
if tfMap == nil {
return nil
}

data := &tftags.DefaultConfig{}

if v, ok := tfMap["tags"].(map[string]interface{}); ok {
data.Tags = tftags.New(ctx, v)
}

return data
}
218 changes: 215 additions & 3 deletions internal/service/s3/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,9 +848,9 @@ func TestAccS3Object_storageClass(t *testing.T) {
func TestAccS3Object_tags(t *testing.T) {
ctx := acctest.Context(t)
var obj1, obj2, obj3, obj4 s3.GetObjectOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_object.object"
key := "test-key"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
Expand Down Expand Up @@ -1107,6 +1107,149 @@ func TestAccS3Object_tagsMultipleSlashes(t *testing.T) {
})
}

func TestAccS3Object_DefaultTags_providerOnly(t *testing.T) {
ctx := acctest.Context(t)
var obj s3.GetObjectOutput
resourceName := "aws_s3_object.object"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3EndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"),
testAccObjectConfig_basic(rName),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"),
resource.TestCheckResourceAttr(resourceName, "tags_all.providerkey1", "providervalue1"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"force_destroy"},
ImportStateId: fmt.Sprintf("s3://%s/test-key", rName),
},
},
})
}

func TestAccS3Object_DefaultTags_providerAndResource(t *testing.T) {
ctx := acctest.Context(t)
var obj s3.GetObjectOutput
resourceName := "aws_s3_object.object"
key := "test-key"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3EndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"),
testAccObjectConfig_tags(rName, key, "stuff"),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
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("providerkey1", "providervalue1"),
testAccObjectConfig_updatedTags(rName, key, "stuff"),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
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 TestAccS3Object_DefaultTags_providerAndResourceWithOverride(t *testing.T) {
ctx := acctest.Context(t)
var obj s3.GetObjectOutput
resourceName := "aws_s3_object.object"
key := "test-key"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3EndpointID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectDestroy(ctx),
Steps: []resource.TestStep{
{
Config: acctest.ConfigCompose(
acctest.ConfigDefaultTags_Tags1("providerkey1", "providervalue1"),
testAccObjectConfig_tagsWithOverride(rName, key, "stuff"),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
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("providerkey1", "providervalue1"),
testAccObjectConfig_updatedTagsWithOverride(rName, key, "stuff"),
),
Check: resource.ComposeTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
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_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 TestAccS3Object_objectLockLegalHoldStartWithNone(t *testing.T) {
ctx := acctest.Context(t)
var obj1, obj2, obj3 s3.GetObjectOutput
Expand Down Expand Up @@ -2039,6 +2182,75 @@ resource "aws_s3_object" "object" {
`, rName, key, content)
}

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

resource "aws_s3_bucket_versioning" "test" {
bucket = aws_s3_bucket.test.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_object" "object" {
# Must have bucket versioning enabled first
bucket = aws_s3_bucket_versioning.test.bucket
key = %[2]q
content = %[3]q

tags = {
Key1 = "A@AA"
Key2 = "BBB"
Key3 = "CCC"
}

override_provider {
default_tags {
tags = {}
}
}
}
`, rName, key, content)
}

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

resource "aws_s3_bucket_versioning" "test" {
bucket = aws_s3_bucket.test.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_object" "object" {
# Must have bucket versioning enabled first
bucket = aws_s3_bucket_versioning.test.bucket
key = %[2]q
content = %[3]q

tags = {
Key2 = "B@BB"
Key3 = "X X"
Key4 = "DDD"
Key5 = "E:/"
}

override_provider {
default_tags {
tags = {}
}
}
}
`, rName, key, content)
}

func testAccObjectConfig_metadata(rName string, metadataKey1, metadataValue1, metadataKey2, metadataValue2 string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
Expand Down Expand Up @@ -2210,7 +2422,7 @@ resource "aws_s3_bucket" "test" {
resource "aws_s3_object" "object" {
bucket = aws_s3_bucket.test.bucket
key = "test-key"
content = %q
content = %[2]q
kms_key_id = aws_kms_key.test.arn
bucket_key_enabled = true
}
Expand Down Expand Up @@ -2246,7 +2458,7 @@ resource "aws_s3_object" "object" {

bucket = aws_s3_bucket.test.bucket
key = "test-key"
content = %q
content = %[2]q
}
`, rName, content)
}
Expand Down
1 change: 1 addition & 0 deletions internal/verify/resource_differ.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ package verify
// * schema.ResourceDiff
// FIXME: can be removed if https://github.com/hashicorp/terraform-plugin-sdk/pull/626/files is merged
type ResourceDiffer interface {
GetOk(string) (interface{}, bool)
HasChange(string) bool
}
Loading
Loading