Skip to content

Commit

Permalink
Add support for replication_configuration of aws_s3_bucket
Browse files Browse the repository at this point in the history
  • Loading branch information
minamijoyo committed Mar 17, 2022
1 parent bc2e74f commit 25c36c1
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ For upgrading AWS provider v4, some rules have not been implemented yet. The cur
- [x] logging
- [x] object_lock_configuration rule
- [x] policy
- [ ] replication_configuration
- [x] replication_configuration
- [x] request_payer
- [x] server_side_encryption_configuration
- [x] versioning
Expand Down
2 changes: 1 addition & 1 deletion filter/awsv4upgrade/aws_s3_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func NewAWSS3BucketFilter() editor.Filter {
&AWSS3BucketLoggingFilter{},
&AWSS3BucketObjectLockConfigurationFilter{},
&AWSS3BucketPolicyFilter{},
// &AWSS3BucketReplicationConfigurationFilter{},
&AWSS3BucketReplicationConfigurationFilter{},
&AWSS3BucketRequestPayerFilter{},
&AWSS3BucketServerSideEncryptionConfigurationFilter{},
&AWSS3BucketVersioningFilter{},
Expand Down
117 changes: 117 additions & 0 deletions filter/awsv4upgrade/aws_s3_bucket_replication_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package awsv4upgrade

import (
"github.com/minamijoyo/tfedit/tfeditor"
"github.com/minamijoyo/tfedit/tfwrite"
)

// AWSS3BucketReplicationConfigurationFilter is a filter implementation for
// upgrading the replication_configuration argument of aws_s3_bucket.
// https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/version-4-upgrade#replication_configuration-argument
type AWSS3BucketReplicationConfigurationFilter struct{}

var _ tfeditor.ResourceFilter = (*AWSS3BucketReplicationConfigurationFilter)(nil)

// NewAWSS3BucketReplicationConfigurationFilter creates a new instance of
// AWSS3BucketReplicationConfigurationFilter.
func NewAWSS3BucketReplicationConfigurationFilter() tfeditor.ResourceFilter {
return &AWSS3BucketReplicationConfigurationFilter{}
}

// ResourceFilter upgrades the replication_configuration argument of
// aws_s3_bucket.
func (f *AWSS3BucketReplicationConfigurationFilter) ResourceFilter(inFile *tfwrite.File, resource *tfwrite.Resource) (*tfwrite.File, error) {
oldNestedBlock := "replication_configuration"
newResourceType := "aws_s3_bucket_replication_configuration"

nestedBlocks := resource.FindNestedBlocksByType(oldNestedBlock)
if len(nestedBlocks) == 0 {
return inFile, nil
}

resourceName := resource.Name()
newResource := tfwrite.NewEmptyResource(newResourceType, resourceName)
inFile.AppendResource(newResource)
setBucketArgument(newResource, resource)

for _, nestedBlock := range nestedBlocks {
roleAttr := nestedBlock.GetAttribute("role")
if roleAttr != nil {
newResource.AppendAttribute(roleAttr)
}

rulesBlocks := nestedBlock.FindNestedBlocksByType("rules")
for _, rulesBlock := range rulesBlocks {
// Rename a `rules` block to a `rule` block
rulesBlock.SetType("rule")
newResource.AppendNestedBlock(rulesBlock)

// Map a `delete_marker_replication_status` attribute to a `delete_marker_replication` block
// delete_marker_replication_status = "Enabled"
// =>
// delete_marker_replication {
// status = "Enabled"
// }
deleteMarkerAttr := rulesBlock.GetAttribute("delete_marker_replication_status")
if deleteMarkerAttr != nil {
deleteMarkerBlock := tfwrite.NewEmptyNestedBlock("delete_marker_replication")
rulesBlock.AppendNestedBlock(deleteMarkerBlock)
deleteMarkerBlock.SetAttributeRaw("status", deleteMarkerAttr.ValueAsTokens())
rulesBlock.RemoveAttribute("delete_marker_replication_status")
}

destinationBlocks := rulesBlock.FindNestedBlocksByType("destination")
for _, destinationBlock := range destinationBlocks {
// Map a `replication_time.minutes` attribute to a `replication_time.time.minutes` attribute
// replication_time {
// status = "Enabled"
// minutes = 15
// }
// =>
// replication_time {
// status = "Enabled"
// time {
// minutes = 15
// }
// }
replicationTimeBlocks := destinationBlock.FindNestedBlocksByType("replication_time")
for _, replicationTimeBlock := range replicationTimeBlocks {
minutesAttribute := replicationTimeBlock.GetAttribute("minutes")
if minutesAttribute != nil {
timeBlock := tfwrite.NewEmptyNestedBlock("time")
replicationTimeBlock.AppendNestedBlock(timeBlock)
timeBlock.SetAttributeRaw("minutes", minutesAttribute.ValueAsTokens())
replicationTimeBlock.RemoveAttribute("minutes")
}
}

// Map a `metrics.minutes` attribute to a `metrics.event_threshold.minutes` attribute
// metrics {
// status = "Enabled"
// minutes = 15
// }
// =>
// metrics {
// status = "Enabled"
// event_threshold {
// minutes = 15
// }
// }
metricsBlocks := destinationBlock.FindNestedBlocksByType("metrics")
for _, metricsBlock := range metricsBlocks {
minutesAttribute := metricsBlock.GetAttribute("minutes")
if minutesAttribute != nil {
eventThresholdBlock := tfwrite.NewEmptyNestedBlock("event_threshold")
metricsBlock.AppendNestedBlock(eventThresholdBlock)
eventThresholdBlock.SetAttributeRaw("minutes", minutesAttribute.ValueAsTokens())
metricsBlock.RemoveAttribute("minutes")
}
}
}
}

resource.RemoveNestedBlock(nestedBlock)
}

return inFile, nil
}
133 changes: 133 additions & 0 deletions filter/awsv4upgrade/aws_s3_bucket_replication_configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package awsv4upgrade

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/minamijoyo/hcledit/editor"
"github.com/minamijoyo/tfedit/tfeditor"
)

func TestAWSS3BucketReplicationConfigurationFilter(t *testing.T) {
cases := []struct {
name string
src string
ok bool
want string
}{
{
name: "simple",
src: `
resource "aws_s3_bucket" "example" {
bucket = "tfedit-test"
replication_configuration {
role = aws_iam_role.replication.arn
rules {
id = "foobar"
status = "Enabled"
filter {}
delete_marker_replication_status = "Enabled"
destination {
bucket = aws_s3_bucket.destination.arn
storage_class = "STANDARD"
replication_time {
status = "Enabled"
minutes = 15
}
metrics {
status = "Enabled"
minutes = 15
}
}
}
}
}
`,
ok: true,
want: `
resource "aws_s3_bucket" "example" {
bucket = "tfedit-test"
}
resource "aws_s3_bucket_replication_configuration" "example" {
bucket = aws_s3_bucket.example.id
role = aws_iam_role.replication.arn
rule {
id = "foobar"
status = "Enabled"
filter {}
destination {
bucket = aws_s3_bucket.destination.arn
storage_class = "STANDARD"
replication_time {
status = "Enabled"
time {
minutes = 15
}
}
metrics {
status = "Enabled"
event_threshold {
minutes = 15
}
}
}
delete_marker_replication {
status = "Enabled"
}
}
}
`,
},
{
name: "argument not found",
src: `
resource "aws_s3_bucket" "example" {
bucket = "tfedit-test"
foo {}
}
`,
ok: true,
want: `
resource "aws_s3_bucket" "example" {
bucket = "tfedit-test"
foo {}
}
`,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
filter := &AWSS3BucketFilter{filters: []tfeditor.ResourceFilter{&AWSS3BucketReplicationConfigurationFilter{}}}
o := editor.NewEditOperator(filter)
output, err := o.Apply([]byte(tc.src), "test")
if tc.ok && err != nil {
t.Fatalf("unexpected err = %s", err)
}

got := string(output)
if !tc.ok && err == nil {
t.Fatalf("expected to return an error, but no error, outStream: \n%s", got)
}

if diff := cmp.Diff(got, tc.want); diff != "" {
t.Fatalf("got:\n%s\nwant:\n%s\ndiff:\n%s", got, tc.want, diff)
}
})
}
}
63 changes: 63 additions & 0 deletions filter/awsv4upgrade/aws_s3_bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,32 @@ resource "aws_s3_bucket" "example" {
}
EOF
replication_configuration {
role = aws_iam_role.replication.arn
rules {
id = "foobar"
status = "Enabled"
filter {}
delete_marker_replication_status = "Enabled"
destination {
bucket = aws_s3_bucket.destination.arn
storage_class = "STANDARD"
replication_time {
status = "Enabled"
minutes = 15
}
metrics {
status = "Enabled"
minutes = 15
}
}
}
}
request_payer = "Requester"
server_side_encryption_configuration {
Expand Down Expand Up @@ -332,6 +358,43 @@ resource "aws_s3_bucket_policy" "example" {
EOF
}
resource "aws_s3_bucket_replication_configuration" "example" {
bucket = aws_s3_bucket.example.id
role = aws_iam_role.replication.arn
rule {
id = "foobar"
status = "Enabled"
filter {}
destination {
bucket = aws_s3_bucket.destination.arn
storage_class = "STANDARD"
replication_time {
status = "Enabled"
time {
minutes = 15
}
}
metrics {
status = "Enabled"
event_threshold {
minutes = 15
}
}
}
delete_marker_replication {
status = "Enabled"
}
}
}
resource "aws_s3_bucket_request_payment_configuration" "example" {
bucket = aws_s3_bucket.example.id
payer = "Requester"
Expand Down

0 comments on commit 25c36c1

Please sign in to comment.