From d4b1dc33714833464a90608acdc0684ecc166cc9 Mon Sep 17 00:00:00 2001 From: Justin DeFrank Date: Fri, 3 Nov 2023 09:40:52 -0400 Subject: [PATCH] r/aws_datasync_task: add `task_report_config` argument (#33861) * upd: introduced functionality to configure datasync task reports * upd: WIP testing for datasync task reports * upd: updated registry documentation for datasync task reports * upd: bumping aws-sdk-go to 1.45.x for datasync task report config options * Revert "upd: bumping aws-sdk-go to 1.45.x for datasync task report config options" This reverts commit 89036cdadbaef4f9a0b055de46c21481abd3a065. * update go.mod * go.mod updated * build out acc test * test case works, need assertions * add additonal test cases * fix test formatting * remove trailing newline * chore: go mod tidy * chore: changelog * r/aws_datasync_task(doc): link to argument reference headers * r/aws_datasync_task: rm update input log * r/aws_datasync_task(test): standardize task report config test naming --------- Co-authored-by: rizkybiz Co-authored-by: Bruce Harrison Co-authored-by: Jared Baker --- .changelog/33861.txt | 3 + internal/service/datasync/task.go | 188 ++++++++++++++++++++- internal/service/datasync/task_test.go | 112 ++++++++++++ website/docs/r/datasync_task.html.markdown | 30 +++- 4 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 .changelog/33861.txt diff --git a/.changelog/33861.txt b/.changelog/33861.txt new file mode 100644 index 000000000000..d8965396d01b --- /dev/null +++ b/.changelog/33861.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_datasync_task: Add `task_report_config` argument +``` diff --git a/internal/service/datasync/task.go b/internal/service/datasync/task.go index a68f55c416db..3ac39fad143b 100644 --- a/internal/service/datasync/task.go +++ b/internal/service/datasync/task.go @@ -222,6 +222,82 @@ func ResourceTask() *schema.Resource { }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), + "task_report_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "s3_destination": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket_access_role_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "s3_bucket_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "subdirectory": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "s3_object_versioning": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ObjectVersionIds_Values(), false), + }, + "output_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportOutputType_Values(), false), + }, + "report_overrides": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "deleted_override": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportLevel_Values(), false), + }, + "skipped_override": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportLevel_Values(), false), + }, + "transferred_override": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportLevel_Values(), false), + }, + "verified_override": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportLevel_Values(), false), + }, + }, + }, + }, + "report_level": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(datasync.ReportLevel_Values(), false), + }, + }, + }, + }, }, CustomizeDiff: verify.SetTagsDiff, @@ -255,6 +331,10 @@ func resourceTaskCreate(ctx context.Context, d *schema.ResourceData, meta interf input.Name = aws.String(v.(string)) } + if v, ok := d.GetOk("task_report_config"); ok { + input.TaskReportConfig = expandTaskReportConfig(v.([]interface{})) + } + if v, ok := d.GetOk("schedule"); ok { input.Schedule = expandTaskSchedule(v.([]interface{})) } @@ -306,6 +386,9 @@ func resourceTaskRead(ctx context.Context, d *schema.ResourceData, meta interfac if err := d.Set("schedule", flattenTaskSchedule(output.Schedule)); err != nil { return sdkdiag.AppendErrorf(diags, "setting schedule: %s", err) } + if err := d.Set("task_report_config", flattenTaskReportConfig(output.TaskReportConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting task_report_config: %s", err) + } d.Set("source_location_arn", output.SourceLocationArn) return diags @@ -344,9 +427,11 @@ func resourceTaskUpdate(ctx context.Context, d *schema.ResourceData, meta interf input.Schedule = expandTaskSchedule(d.Get("schedule").([]interface{})) } - _, err := conn.UpdateTaskWithContext(ctx, input) + if d.HasChanges("task_report_config") { + input.TaskReportConfig = expandTaskReportConfig(d.Get("task_report_config").([]interface{})) + } - if err != nil { + if _, err := conn.UpdateTaskWithContext(ctx, input); err != nil { return sdkdiag.AppendErrorf(diags, "updating DataSync Task (%s): %s", d.Id(), err) } } @@ -462,6 +547,51 @@ func flattenOptions(options *datasync.Options) []interface{} { return []interface{}{m} } +func flattenTaskReportConfig(options *datasync.TaskReportConfig) []interface{} { + if options == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "s3_object_versioning": aws.StringValue(options.ObjectVersionIds), + "output_type": aws.StringValue(options.OutputType), + "report_level": aws.StringValue(options.ReportLevel), + "s3_destination": flattenTaskReportConfigS3Destination(options.Destination.S3), + "report_overrides": flattenTaskReportConfigReportOverrides(options.Overrides), + } + + return []interface{}{m} +} + +func flattenTaskReportConfigReportOverrides(options *datasync.ReportOverrides) []interface{} { + if options == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "deleted_override": aws.StringValue(options.Deleted.ReportLevel), + "skipped_override": aws.StringValue(options.Skipped.ReportLevel), + "transferred_override": aws.StringValue(options.Transferred.ReportLevel), + "verified_override": aws.StringValue(options.Verified.ReportLevel), + } + + return []interface{}{m} +} + +func flattenTaskReportConfigS3Destination(options *datasync.ReportDestinationS3) []interface{} { + if options == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "bucket_access_role_arn": aws.StringValue(options.BucketAccessRoleArn), + "s3_bucket_arn": aws.StringValue(options.S3BucketArn), + "subdirectory": aws.StringValue(options.Subdirectory), + } + + return []interface{}{m} +} + func expandOptions(l []interface{}) *datasync.Options { if len(l) == 0 || l[0] == nil { return nil @@ -522,6 +652,60 @@ func flattenTaskSchedule(schedule *datasync.TaskSchedule) []interface{} { return []interface{}{m} } +func expandTaskReportConfig(l []interface{}) *datasync.TaskReportConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + reportConfig := &datasync.TaskReportConfig{} + + m := l[0].(map[string]interface{}) + + dest := m["s3_destination"].([]interface{}) + reportConfig = reportConfig.SetDestination(expandTaskReportDestination(dest)) + reportConfig = reportConfig.SetObjectVersionIds(m["s3_object_versioning"].(string)) + reportConfig = reportConfig.SetOutputType(m["output_type"].(string)) + reportConfig = reportConfig.SetReportLevel(m["report_level"].(string)) + o := m["report_overrides"].([]interface{}) + reportConfig = reportConfig.SetOverrides(expandTaskReportOverrides(o)) + + return reportConfig +} + +func expandTaskReportDestination(l []interface{}) *datasync.ReportDestination { + if len(l) == 0 || l[0] == nil { + return nil + } + m := l[0].(map[string]interface{}) + return &datasync.ReportDestination{ + S3: &datasync.ReportDestinationS3{ + BucketAccessRoleArn: aws.String(m["bucket_access_role_arn"].(string)), + S3BucketArn: aws.String(m["s3_bucket_arn"].(string)), + Subdirectory: aws.String(m["subdirectory"].(string)), + }, + } +} + +func expandTaskReportOverrides(l []interface{}) *datasync.ReportOverrides { + if len(l) == 0 || l[0] == nil { + return nil + } + m := l[0].(map[string]interface{}) + return &datasync.ReportOverrides{ + Deleted: &datasync.ReportOverride{ + ReportLevel: aws.String(m["deleted_override"].(string)), + }, + Skipped: &datasync.ReportOverride{ + ReportLevel: aws.String(m["skipped_override"].(string)), + }, + Transferred: &datasync.ReportOverride{ + ReportLevel: aws.String(m["transferred_override"].(string)), + }, + Verified: &datasync.ReportOverride{ + ReportLevel: aws.String(m["verified_override"].(string)), + }, + } +} + func expandFilterRules(l []interface{}) []*datasync.FilterRule { filterRules := []*datasync.FilterRule{} diff --git a/internal/service/datasync/task_test.go b/internal/service/datasync/task_test.go index f13523ae01b3..db7d236f3abb 100644 --- a/internal/service/datasync/task_test.go +++ b/internal/service/datasync/task_test.go @@ -798,6 +798,46 @@ func TestAccDataSyncTask_DefaultSyncOptions_verifyMode(t *testing.T) { }) } +func TestAccDataSyncTask_taskReportConfig(t *testing.T) { + ctx := acctest.Context(t) + var task1 datasync.DescribeTaskOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_datasync_task.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, datasync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckTaskDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccTaskConfig_taskReportConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTaskExists(ctx, resourceName, &task1), + resource.TestCheckResourceAttr(resourceName, "task_report_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.output_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_level", "SUCCESSES_AND_ERRORS"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.s3_destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.s3_object_versioning", "INCLUDE"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.s3_destination.0.subdirectory", "test/"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_overrides.#", "1"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_overrides.0.deleted_override", "ERRORS_ONLY"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_overrides.0.skipped_override", "ERRORS_ONLY"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_overrides.0.transferred_override", "ERRORS_ONLY"), + resource.TestCheckResourceAttr(resourceName, "task_report_config.0.report_overrides.0.verified_override", "ERRORS_ONLY"), + resource.TestCheckResourceAttrPair(resourceName, "task_report_config.0.s3_destination.0.bucket_access_role_arn", "aws_iam_role.report_test", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "task_report_config.0.s3_destination.0.s3_bucket_arn", "aws_s3_bucket.report_test", "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccDataSyncTask_tags(t *testing.T) { ctx := acctest.Context(t) var task1, task2, task3 datasync.DescribeTaskOutput @@ -1463,3 +1503,75 @@ resource "aws_datasync_task" "test" { } `, rName, key1, value1, key2, value2)) } + +func testAccTaskConfig_taskReportConfig(rName string) string { + return acctest.ConfigCompose( + testAccTaskConfig_baseLocationS3(rName), + testAccTaskConfig_baseLocationNFS(rName), + fmt.Sprintf(` +resource "aws_s3_bucket" "report_test" { + bucket = "%[1]s-report-test" + force_destroy = true +} + +resource "aws_iam_role" "report_test" { + name = "%[1]s-report-test" + assume_role_policy = <