From af77d84ce67a301178b55fa8aa994c44e0ec738b Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Thu, 11 Feb 2021 21:12:30 +0200 Subject: [PATCH] resource/aws_codebuild_report_group: Add delete_reports argument and service package refactoring (#17338) Output from acceptance testing in AWS Commercial: ``` --- PASS: TestAccAWSCodeBuildReportGroup_disappears (10.64s) --- PASS: TestAccAWSCodeBuildReportGroup_deleteReports (13.91s) --- PASS: TestAccAWSCodeBuildReportGroup_basic (14.42s) --- PASS: TestAccAWSCodeBuildReportGroup_tags (30.94s) --- PASS: TestAccAWSCodeBuildReportGroup_export_s3 (46.32s) ``` Output from acceptance testing in AWS GovCloud (US): ``` --- PASS: TestAccAWSCodeBuildReportGroup_disappears (14.58s) --- PASS: TestAccAWSCodeBuildReportGroup_basic (19.24s) --- PASS: TestAccAWSCodeBuildReportGroup_deleteReports (19.27s) --- PASS: TestAccAWSCodeBuildReportGroup_tags (44.22s) --- PASS: TestAccAWSCodeBuildReportGroup_export_s3 (50.66s) ``` --- .changelog/17338.txt | 3 + .../service/codebuild/finder/finder.go | 32 ++++ .../service/codebuild/waiter/status.go | 29 ++++ .../service/codebuild/waiter/waiter.go | 31 ++++ aws/resource_aws_codebuild_report_group.go | 34 +++-- ...esource_aws_codebuild_report_group_test.go | 141 ++++++++++++++---- .../r/codebuild_report_group.html.markdown | 1 + 7 files changed, 230 insertions(+), 41 deletions(-) create mode 100644 .changelog/17338.txt create mode 100644 aws/internal/service/codebuild/finder/finder.go create mode 100644 aws/internal/service/codebuild/waiter/status.go create mode 100644 aws/internal/service/codebuild/waiter/waiter.go diff --git a/.changelog/17338.txt b/.changelog/17338.txt new file mode 100644 index 00000000000..6d07bb41a4d --- /dev/null +++ b/.changelog/17338.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_codebuild_report_group: Add `delete_reports` argument +``` diff --git a/aws/internal/service/codebuild/finder/finder.go b/aws/internal/service/codebuild/finder/finder.go new file mode 100644 index 00000000000..7c8c4cd8ec7 --- /dev/null +++ b/aws/internal/service/codebuild/finder/finder.go @@ -0,0 +1,32 @@ +package finder + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codebuild" +) + +// ReportGroupByArn returns the Report Group corresponding to the specified Arn. +func ReportGroupByArn(conn *codebuild.CodeBuild, arn string) (*codebuild.ReportGroup, error) { + + output, err := conn.BatchGetReportGroups(&codebuild.BatchGetReportGroupsInput{ + ReportGroupArns: aws.StringSlice([]string{arn}), + }) + if err != nil { + return nil, err + } + + if output == nil { + return nil, nil + } + + if len(output.ReportGroups) == 0 { + return nil, nil + } + + reportGroup := output.ReportGroups[0] + if reportGroup == nil { + return nil, nil + } + + return reportGroup, nil +} diff --git a/aws/internal/service/codebuild/waiter/status.go b/aws/internal/service/codebuild/waiter/status.go new file mode 100644 index 00000000000..041e9a31c80 --- /dev/null +++ b/aws/internal/service/codebuild/waiter/status.go @@ -0,0 +1,29 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codebuild" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/codebuild/finder" +) + +const ( + ReportGroupStatusUnknown = "Unknown" + ReportGroupStatusNotFound = "NotFound" +) + +// ReportGroupStatus fetches the Report Group and its Status +func ReportGroupStatus(conn *codebuild.CodeBuild, arn string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := finder.ReportGroupByArn(conn, arn) + if err != nil { + return nil, ReportGroupStatusUnknown, err + } + + if output == nil { + return nil, ReportGroupStatusNotFound, nil + } + + return output, aws.StringValue(output.Status), nil + } +} diff --git a/aws/internal/service/codebuild/waiter/waiter.go b/aws/internal/service/codebuild/waiter/waiter.go new file mode 100644 index 00000000000..29a2f7c9c90 --- /dev/null +++ b/aws/internal/service/codebuild/waiter/waiter.go @@ -0,0 +1,31 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/codebuild" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + // Maximum amount of time to wait for an Operation to return Deleted + ReportGroupDeleteTimeout = 2 * time.Minute +) + +// ReportGroupDeleted waits for an ReportGroup to return Deleted +func ReportGroupDeleted(conn *codebuild.CodeBuild, arn string) (*codebuild.ReportGroup, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{codebuild.ReportGroupStatusTypeDeleting}, + Target: []string{}, + Refresh: ReportGroupStatus(conn, arn), + Timeout: ReportGroupDeleteTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*codebuild.ReportGroup); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_codebuild_report_group.go b/aws/resource_aws_codebuild_report_group.go index 0d86b56170c..7007196927b 100644 --- a/aws/resource_aws_codebuild_report_group.go +++ b/aws/resource_aws_codebuild_report_group.go @@ -10,6 +10,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/codebuild/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/codebuild/waiter" ) func resourceAwsCodeBuildReportGroup() *schema.Resource { @@ -90,6 +92,11 @@ func resourceAwsCodeBuildReportGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "delete_reports": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "tags": tagsSchema(), }, } @@ -106,7 +113,7 @@ func resourceAwsCodeBuildReportGroupCreate(d *schema.ResourceData, meta interfac resp, err := conn.CreateReportGroup(createOpts) if err != nil { - return fmt.Errorf("error creating CodeBuild Report Groups: %w", err) + return fmt.Errorf("error creating CodeBuild Report Group: %w", err) } d.SetId(aws.StringValue(resp.ReportGroup.Arn)) @@ -118,23 +125,13 @@ func resourceAwsCodeBuildReportGroupRead(d *schema.ResourceData, meta interface{ conn := meta.(*AWSClient).codebuildconn ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - resp, err := conn.BatchGetReportGroups(&codebuild.BatchGetReportGroupsInput{ - ReportGroupArns: aws.StringSlice([]string{d.Id()}), - }) + reportGroup, err := finder.ReportGroupByArn(conn, d.Id()) if err != nil { return fmt.Errorf("error Listing CodeBuild Report Groups: %w", err) } - if len(resp.ReportGroups) == 0 { - log.Printf("[WARN] CodeBuild Report Groups (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - reportGroup := resp.ReportGroups[0] - if reportGroup == nil { - log.Printf("[WARN] CodeBuild Report Groups (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] CodeBuild Report Group (%s) not found, removing from state", d.Id()) d.SetId("") return nil } @@ -175,7 +172,7 @@ func resourceAwsCodeBuildReportGroupUpdate(d *schema.ResourceData, meta interfac _, err := conn.UpdateReportGroup(input) if err != nil { - return fmt.Errorf("error updating CodeBuild Report Groups: %w", err) + return fmt.Errorf("error updating CodeBuild Report Group: %w", err) } return resourceAwsCodeBuildReportGroupRead(d, meta) @@ -185,11 +182,16 @@ func resourceAwsCodeBuildReportGroupDelete(d *schema.ResourceData, meta interfac conn := meta.(*AWSClient).codebuildconn deleteOpts := &codebuild.DeleteReportGroupInput{ - Arn: aws.String(d.Id()), + Arn: aws.String(d.Id()), + DeleteReports: aws.Bool(d.Get("delete_reports").(bool)), } if _, err := conn.DeleteReportGroup(deleteOpts); err != nil { - return fmt.Errorf("error deleting CodeBuild Report Groups(%s): %w", d.Id(), err) + return fmt.Errorf("error deleting CodeBuild Report Group (%s): %w", d.Id(), err) + } + + if _, err := waiter.ReportGroupDeleted(conn, d.Id()); err != nil { + return fmt.Errorf("error while waiting for CodeBuild Report Group (%s) to become deleted: %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_codebuild_report_group_test.go b/aws/resource_aws_codebuild_report_group_test.go index a42b6cc4758..625eb14ab4c 100644 --- a/aws/resource_aws_codebuild_report_group_test.go +++ b/aws/resource_aws_codebuild_report_group_test.go @@ -2,15 +2,72 @@ package aws import ( "fmt" + "log" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/codebuild" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/codebuild/finder" ) +func init() { + resource.AddTestSweepers("aws_codebuild_report_group", &resource.Sweeper{ + Name: "aws_codebuild_report_group", + F: testSweepCodeBuildReportGroups, + }) +} + +func testSweepCodeBuildReportGroups(region string) error { + client, err := sharedClientForRegion(region) + + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + + conn := client.(*AWSClient).codebuildconn + input := &codebuild.ListReportGroupsInput{} + var sweeperErrs *multierror.Error + + err = conn.ListReportGroupsPages(input, func(page *codebuild.ListReportGroupsOutput, isLast bool) bool { + if page == nil { + return !isLast + } + + for _, arn := range page.ReportGroups { + id := aws.StringValue(arn) + r := resourceAwsCodeBuildReportGroup() + d := r.Data(nil) + d.SetId(id) + d.Set("delete_reports", true) + + err := r.Delete(d, client) + if err != nil { + sweeperErr := fmt.Errorf("error deleting CodeBuild Report Group (%s): %w", id, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue + } + } + + return !isLast + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping CodeBuild Report Group sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving CodeBuild ReportGroups: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + func TestAccAWSCodeBuildReportGroup_basic(t *testing.T) { var reportGroup codebuild.ReportGroup rName := acctest.RandomWithPrefix("tf-acc-test") @@ -33,9 +90,10 @@ func TestAccAWSCodeBuildReportGroup_basic(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"delete_reports"}, }, }, }) @@ -67,9 +125,10 @@ func TestAccAWSCodeBuildReportGroup_export_s3(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"delete_reports"}, }, { Config: testAccAWSCodeBuildReportGroupS3ExportUpdatedConfig(rName), @@ -108,9 +167,10 @@ func TestAccAWSCodeBuildReportGroup_tags(t *testing.T) { ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"delete_reports"}, }, { Config: testAccAWSCodeBuildReportGroupConfigTags2(rName, "key1", "value1updated", "key2", "value2"), @@ -133,6 +193,33 @@ func TestAccAWSCodeBuildReportGroup_tags(t *testing.T) { }) } +func TestAccAWSCodeBuildReportGroup_deleteReports(t *testing.T) { + var reportGroup codebuild.ReportGroup + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_codebuild_report_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSCodeBuildReportGroup(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeBuildReportGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCodeBuildReportGroupDeleteReportsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeBuildReportGroupExists(resourceName, &reportGroup), + resource.TestCheckResourceAttr(resourceName, "name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"delete_reports"}, + }, + }, + }) +} + func TestAccAWSCodeBuildReportGroup_disappears(t *testing.T) { var reportGroup codebuild.ReportGroup rName := acctest.RandomWithPrefix("tf-acc-test") @@ -179,22 +266,15 @@ func testAccCheckAWSCodeBuildReportGroupDestroy(s *terraform.State) error { continue } - resp, err := conn.BatchGetReportGroups(&codebuild.BatchGetReportGroupsInput{ - ReportGroupArns: aws.StringSlice([]string{rs.Primary.ID}), - }) + resp, err := finder.ReportGroupByArn(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.ReportGroups) == 0 { - return nil + if resp != nil { + return fmt.Errorf("Found Report Group %s", rs.Primary.ID) } - for _, reportGroup := range resp.ReportGroups { - if rs.Primary.ID == aws.StringValue(reportGroup.Arn) { - return fmt.Errorf("Found Report Groups %s", rs.Primary.ID) - } - } } return nil } @@ -208,19 +288,16 @@ func testAccCheckAWSCodeBuildReportGroupExists(name string, reportGroup *codebui conn := testAccProvider.Meta().(*AWSClient).codebuildconn - resp, err := conn.BatchGetReportGroups(&codebuild.BatchGetReportGroupsInput{ - ReportGroupArns: aws.StringSlice([]string{rs.Primary.ID}), - }) + resp, err := finder.ReportGroupByArn(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.ReportGroups) != 1 || - aws.StringValue(resp.ReportGroups[0].Arn) != rs.Primary.ID { + if resp == nil { return fmt.Errorf("Report Group %s not found", rs.Primary.ID) } - *reportGroup = *resp.ReportGroups[0] + *reportGroup = *resp return nil } @@ -348,3 +425,17 @@ resource "aws_codebuild_report_group" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2) } + +func testAccAWSCodeBuildReportGroupDeleteReportsConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_codebuild_report_group" "test" { + name = %[1]q + type = "TEST" + delete_reports = true + + export_config { + type = "NO_EXPORT" + } +} +`, rName) +} diff --git a/website/docs/r/codebuild_report_group.html.markdown b/website/docs/r/codebuild_report_group.html.markdown index 6c120fc5410..952471015bf 100644 --- a/website/docs/r/codebuild_report_group.html.markdown +++ b/website/docs/r/codebuild_report_group.html.markdown @@ -65,6 +65,7 @@ The following arguments are supported: * `name` - (Required) The name of a Report Group. * `type` - (Required) The type of the Report Group. Valid value are `TEST` and `CODE_COVERAGE`. * `export_config` - (Required) Information about the destination where the raw data of this Report Group is exported. see [Export Config](#export-config) documented below. +* `delete_reports` - (Optional) If `true`, deletes any reports that belong to a report group before deleting the report group. If `false`, you must delete any reports in the report group before deleting it. Default value is `false`. * `tags` - (Optional) Key-value mapping of resource tags ### Export Config