From fc68bb7560e57b8ef7372aabe2e58d85d264eecd Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 25 Nov 2021 12:01:45 +0200 Subject: [PATCH 1/7] cloud9 env ec2 refactor + sweep --- internal/service/cloud9/environment_ec2.go | 137 ++++++------------ .../service/cloud9/environment_ec2_test.go | 85 ++--------- internal/service/cloud9/find.go | 41 ++++++ internal/service/cloud9/status.go | 31 ++++ internal/service/cloud9/sweep.go | 72 +++++++++ internal/service/cloud9/wait.go | 47 ++++++ 6 files changed, 246 insertions(+), 167 deletions(-) create mode 100644 internal/service/cloud9/find.go create mode 100644 internal/service/cloud9/status.go create mode 100644 internal/service/cloud9/sweep.go create mode 100644 internal/service/cloud9/wait.go diff --git a/internal/service/cloud9/environment_ec2.go b/internal/service/cloud9/environment_ec2.go index 79949481c96..545d733e47a 100644 --- a/internal/service/cloud9/environment_ec2.go +++ b/internal/service/cloud9/environment_ec2.go @@ -3,13 +3,13 @@ package cloud9 import ( "fmt" "log" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloud9" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -30,8 +30,9 @@ func ResourceEnvironmentEC2() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 60), }, "instance_type": { Type: schema.TypeString, @@ -39,13 +40,15 @@ func ResourceEnvironmentEC2() *schema.Resource { ForceNew: true, }, "automatic_stop_time_minutes": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntAtMost(20160), }, "description": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 200), }, "owner_arn": { Type: schema.TypeString, @@ -118,39 +121,13 @@ func resourceEnvironmentEC2Create(d *schema.ResourceData, meta interface{}) erro } if err != nil { - return fmt.Errorf("Error creating Cloud9 EC2 Environment: %s", err) + return fmt.Errorf("Error creating Cloud9 EC2 Environment: %w", err) } d.SetId(aws.StringValue(out.EnvironmentId)) - stateConf := resource.StateChangeConf{ - Pending: []string{ - cloud9.EnvironmentStatusConnecting, - cloud9.EnvironmentStatusCreating, - }, - Target: []string{ - cloud9.EnvironmentStatusReady, - }, - Timeout: 10 * time.Minute, - Refresh: func() (interface{}, string, error) { - out, err := conn.DescribeEnvironmentStatus(&cloud9.DescribeEnvironmentStatusInput{ - EnvironmentId: aws.String(d.Id()), - }) - if err != nil { - return 42, "", err - } - - status := aws.StringValue(out.Status) - - if status == cloud9.EnvironmentStatusError && out.Message != nil { - return out, status, fmt.Errorf("Reason: %s", aws.StringValue(out.Message)) - } - - return out, status, nil - }, - } - _, err = stateConf.WaitForState() + _, err = waitEnvironmentReady(conn, d.Id()) if err != nil { - return err + return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) to be ready: %w", d.Id(), err) } return resourceEnvironmentEC2Read(d, meta) @@ -163,23 +140,17 @@ func resourceEnvironmentEC2Read(d *schema.ResourceData, meta interface{}) error log.Printf("[INFO] Reading Cloud9 Environment EC2 %s", d.Id()) - out, err := conn.DescribeEnvironments(&cloud9.DescribeEnvironmentsInput{ - EnvironmentIds: []*string{aws.String(d.Id())}, - }) - if err != nil { - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - log.Printf("[WARN] Cloud9 Environment EC2 (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err - } - if len(out.Environments) == 0 { + env, err := FindEnvironmentByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Cloud9 Environment EC2 (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - env := out.Environments[0] + + if err != nil { + return fmt.Errorf("error reading Cloud9 Environment EC2 (%s): %w", d.Id(), err) + } arn := aws.StringValue(env.Arn) d.Set("arn", arn) @@ -191,7 +162,7 @@ func resourceEnvironmentEC2Read(d *schema.ResourceData, meta interface{}) error tags, err := ListTags(conn, arn) if err != nil { - return fmt.Errorf("error listing tags for Cloud9 EC2 Environment (%s): %s", arn, err) + return fmt.Errorf("error listing tags for Cloud9 EC2 Environment (%s): %w", arn, err) } tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) @@ -213,27 +184,29 @@ func resourceEnvironmentEC2Read(d *schema.ResourceData, meta interface{}) error func resourceEnvironmentEC2Update(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).Cloud9Conn - input := cloud9.UpdateEnvironmentInput{ - Description: aws.String(d.Get("description").(string)), - EnvironmentId: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - } + if d.HasChangesExcept("tags_all", "tags") { + input := cloud9.UpdateEnvironmentInput{ + Description: aws.String(d.Get("description").(string)), + EnvironmentId: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + } - log.Printf("[INFO] Updating Cloud9 Environment EC2: %s", input) + log.Printf("[INFO] Updating Cloud9 Environment EC2: %s", input) - out, err := conn.UpdateEnvironment(&input) - if err != nil { - return err - } + out, err := conn.UpdateEnvironment(&input) + if err != nil { + return err + } - log.Printf("[DEBUG] Cloud9 Environment EC2 updated: %s", out) + log.Printf("[DEBUG] Cloud9 Environment EC2 updated: %s", out) + } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") arn := d.Get("arn").(string) if err := UpdateTags(conn, arn, o, n); err != nil { - return fmt.Errorf("error updating Cloud9 EC2 Environment (%s) tags: %s", arn, err) + return fmt.Errorf("error updating Cloud9 EC2 Environment (%s) tags: %w", arn, err) } } @@ -246,42 +219,18 @@ func resourceEnvironmentEC2Delete(d *schema.ResourceData, meta interface{}) erro _, err := conn.DeleteEnvironment(&cloud9.DeleteEnvironmentInput{ EnvironmentId: aws.String(d.Id()), }) + if tfawserr.ErrCodeEquals(err, cloud9.ErrCodeNotFoundException) { + return nil + } + if err != nil { return err } - input := &cloud9.DescribeEnvironmentsInput{ - EnvironmentIds: []*string{aws.String(d.Id())}, - } - var out *cloud9.DescribeEnvironmentsOutput - err = resource.Retry(20*time.Minute, func() *resource.RetryError { // Deleting instances can take a long time - out, err = conn.DescribeEnvironments(input) - if err != nil { - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - return nil - } - // :'-( - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access this resource") { - return nil - } - return resource.NonRetryableError(err) - } - if len(out.Environments) == 0 { - return nil - } - return resource.RetryableError(fmt.Errorf("Cloud9 EC2 Environment %q still exists", d.Id())) - }) - if tfresource.TimedOut(err) { - out, err = conn.DescribeEnvironments(input) - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - return nil - } - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access this resource") { - return nil - } - } + _, err = waitEnvironmentDeleted(conn, d.Id()) if err != nil { - return fmt.Errorf("Error deleting Cloud9 EC2 Environment: %s", err) + return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) to be deleted: %w", d.Id(), err) } + return nil } diff --git a/internal/service/cloud9/environment_ec2_test.go b/internal/service/cloud9/environment_ec2_test.go index 0a93afc3de9..e6da44fa33f 100644 --- a/internal/service/cloud9/environment_ec2_test.go +++ b/internal/service/cloud9/environment_ec2_test.go @@ -4,16 +4,15 @@ import ( "fmt" "regexp" "testing" - "time" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloud9" - "github.com/hashicorp/aws-sdk-go-base/tfawserr" sdkacctest "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/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfcloud9 "github.com/hashicorp/terraform-provider-aws/internal/service/cloud9" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccCloud9EnvironmentEC2_basic(t *testing.T) { @@ -172,7 +171,8 @@ func TestAccCloud9EnvironmentEC2_disappears(t *testing.T) { Config: testAccEnvironmentEC2Config(rName), Check: resource.ComposeTestCheckFunc( testAccCheckEnvironmentEC2Exists(resourceName, &conf), - testAccCheckEnvironmentEC2Disappears(&conf), + acctest.CheckResourceDisappears(acctest.Provider, tfcloud9.ResourceEnvironmentEC2(), resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfcloud9.ResourceEnvironmentEC2(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -193,63 +193,17 @@ func testAccCheckEnvironmentEC2Exists(n string, res *cloud9.Environment) resourc conn := acctest.Provider.Meta().(*conns.AWSClient).Cloud9Conn - out, err := conn.DescribeEnvironments(&cloud9.DescribeEnvironmentsInput{ - EnvironmentIds: []*string{aws.String(rs.Primary.ID)}, - }) + out, err := tfcloud9.FindEnvironmentByID(conn, rs.Primary.ID) if err != nil { - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - return fmt.Errorf("Cloud9 Environment EC2 (%q) not found", rs.Primary.ID) - } return err } - if len(out.Environments) == 0 { - return fmt.Errorf("Cloud9 Environment EC2 (%q) not found", rs.Primary.ID) - } - env := out.Environments[0] - *res = *env + *res = *out return nil } } -func testAccCheckEnvironmentEC2Disappears(res *cloud9.Environment) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).Cloud9Conn - - _, err := conn.DeleteEnvironment(&cloud9.DeleteEnvironmentInput{ - EnvironmentId: res.Id, - }) - - if err != nil { - return err - } - - input := &cloud9.DescribeEnvironmentsInput{ - EnvironmentIds: []*string{res.Id}, - } - var out *cloud9.DescribeEnvironmentsOutput - err = resource.Retry(20*time.Minute, func() *resource.RetryError { // Deleting instances can take a long time - out, err = conn.DescribeEnvironments(input) - if err != nil { - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - return nil - } - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access this resource") { - return nil - } - return resource.NonRetryableError(err) - } - if len(out.Environments) == 0 { - return nil - } - return resource.RetryableError(fmt.Errorf("Cloud9 EC2 Environment %q still exists", aws.StringValue(res.Id))) - }) - - return err - } -} - func testAccCheckEnvironmentEC2Destroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).Cloud9Conn @@ -258,22 +212,15 @@ func testAccCheckEnvironmentEC2Destroy(s *terraform.State) error { continue } - out, err := conn.DescribeEnvironments(&cloud9.DescribeEnvironmentsInput{ - EnvironmentIds: []*string{aws.String(rs.Primary.ID)}, - }) + _, err := tfcloud9.FindEnvironmentByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + if err != nil { - if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "") { - return nil - } - // :'-( - if tfawserr.ErrMessageContains(err, "AccessDeniedException", "is not authorized to access this resource") { - return nil - } return err } - if len(out.Environments) == 0 { - return nil - } return fmt.Errorf("Cloud9 Environment EC2 %q still exists.", rs.Primary.ID) } @@ -330,8 +277,6 @@ resource "aws_route" "test" { func testAccEnvironmentEC2Config(name string) string { return testAccEnvironmentEC2BaseConfig() + fmt.Sprintf(` resource "aws_cloud9_environment_ec2" "test" { - depends_on = [aws_route.test] - instance_type = "t2.micro" name = %[1]q subnet_id = aws_subnet.test.id @@ -345,8 +290,6 @@ data "aws_caller_identity" "current" {} func testAccEnvironmentEC2AllFieldsConfig(name, description, userName string) string { return testAccEnvironmentEC2BaseConfig() + fmt.Sprintf(` resource "aws_cloud9_environment_ec2" "test" { - depends_on = [aws_route.test] - automatic_stop_time_minutes = 60 description = %[2]q instance_type = "t2.micro" @@ -364,8 +307,6 @@ resource "aws_iam_user" "test" { func testAccEnvironmentEC2Tags1Config(name, tagKey1, tagValue1 string) string { return testAccEnvironmentEC2BaseConfig() + fmt.Sprintf(` resource "aws_cloud9_environment_ec2" "test" { - depends_on = [aws_route.test] - instance_type = "t2.micro" name = %[1]q subnet_id = aws_subnet.test.id @@ -380,8 +321,6 @@ resource "aws_cloud9_environment_ec2" "test" { func testAccEnvironmentEC2Tags2Config(name, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return testAccEnvironmentEC2BaseConfig() + fmt.Sprintf(` resource "aws_cloud9_environment_ec2" "test" { - depends_on = [aws_route.test] - instance_type = "t2.micro" name = %[1]q subnet_id = aws_subnet.test.id diff --git a/internal/service/cloud9/find.go b/internal/service/cloud9/find.go new file mode 100644 index 00000000000..d0bac992da0 --- /dev/null +++ b/internal/service/cloud9/find.go @@ -0,0 +1,41 @@ +package cloud9 + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloud9" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func FindEnvironmentByID(conn *cloud9.Cloud9, id string) (*cloud9.Environment, error) { + input := &cloud9.DescribeEnvironmentsInput{ + EnvironmentIds: []*string{aws.String(id)}, + } + out, err := conn.DescribeEnvironments(input) + + if tfawserr.ErrCodeEquals(err, cloud9.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + envs := out.Environments + + if envs == nil || len(envs) == 0 { + return nil, tfresource.NewEmptyResultError(input) + } + + env := envs[0] + + if env == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return env, nil +} diff --git a/internal/service/cloud9/status.go b/internal/service/cloud9/status.go new file mode 100644 index 00000000000..8e263f10f61 --- /dev/null +++ b/internal/service/cloud9/status.go @@ -0,0 +1,31 @@ +package cloud9 + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloud9" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func statusEnvironmentStatus(conn *cloud9.Cloud9, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := FindEnvironmentByID(conn, id) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + lifecycle := out.Lifecycle + status := aws.StringValue(lifecycle.Status) + if status == cloud9.EnvironmentStatusError && lifecycle.Reason != nil { + return out, status, fmt.Errorf("Reason: %s", aws.StringValue(lifecycle.Reason)) + } + + return out, status, nil + } +} diff --git a/internal/service/cloud9/sweep.go b/internal/service/cloud9/sweep.go new file mode 100644 index 00000000000..5b7bc20c1ff --- /dev/null +++ b/internal/service/cloud9/sweep.go @@ -0,0 +1,72 @@ +//go:build sweep +// +build sweep + +package cloud9 + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloud9" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/sweep" +) + +func init() { + resource.AddTestSweepers("aws_cloud9_environment_ec2", &resource.Sweeper{ + Name: "aws_cloud9_environment_ec2", + F: sweepEnvironmentEC2s, + }) +} + +func sweepEnvironmentEC2s(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + conn := client.(*AWSClient).cloud9conn + sweepResources := make([]*testSweepResource, 0) + var sweeperErrs *multierror.Error + + input := &cloud9.ListEnvironmentsInput{} + err = conn.ListEnvironmentsPages(input, func(page *cloud9.ListEnvironmentsOutput, lastPage bool) bool { + if len(page.EnvironmentIds) == 0 { + log.Printf("[INFO] No Cloud9 Environment EC2s to sweep") + return false + } + for _, envID := range page.EnvironmentIds { + id := aws.StringValue(envID) + + log.Printf("[INFO] Deleting Cloud9 Environment EC2: %s", id) + r := ResourceEnvironmentEC2() + d := r.Data(nil) + d.SetId(id) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) + } + return !lastPage + }) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Cloud9 Environment EC2s: %w", err)) + } + + if err = testSweepResourceOrchestrator(sweepResources); err != nil { + errs = multierror.Append(errs, fmt.Errorf("error sweeping Cloud9 Environment EC2 for %s: %w", region, err)) + } + + if testSweepSkipSweepError(errs.ErrorOrNil()) { + log.Printf("[WARN] Skipping Cloud9 Environment EC2s for %s: %s", region, errs) + return nil + } + + return sweeperErrs.ErrorOrNil() +} diff --git a/internal/service/cloud9/wait.go b/internal/service/cloud9/wait.go new file mode 100644 index 00000000000..893f682d004 --- /dev/null +++ b/internal/service/cloud9/wait.go @@ -0,0 +1,47 @@ +package cloud9 + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/cloud9" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + EnvironmentReadyTimeout = 10 * time.Minute + EnvironmentDeletedTimeout = 20 * time.Minute +) + +func waitEnvironmentReady(conn *cloud9.Cloud9, id string) (*cloud9.DescribeEnvironmentStatusOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{cloud9.EnvironmentLifecycleStatusCreating}, + Target: []string{cloud9.EnvironmentLifecycleStatusCreated}, + Refresh: statusEnvironmentStatus(conn, id), + Timeout: EnvironmentReadyTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*cloud9.DescribeEnvironmentStatusOutput); ok { + return output, err + } + + return nil, err +} + +func waitEnvironmentDeleted(conn *cloud9.Cloud9, id string) (*cloud9.DescribeEnvironmentStatusOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{cloud9.EnvironmentLifecycleStatusDeleting}, + Target: []string{}, + Refresh: statusEnvironmentStatus(conn, id), + Timeout: EnvironmentDeletedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*cloud9.DescribeEnvironmentStatusOutput); ok { + return output, err + } + + return nil, err +} From fbf476e13e1cf0f523b47c514594d6e8205fcbbf Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 25 Nov 2021 12:04:17 +0200 Subject: [PATCH 2/7] ec2 sweep dep --- internal/service/ec2/sweep.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/ec2/sweep.go b/internal/service/ec2/sweep.go index 7f543b29df7..47934a017f2 100644 --- a/internal/service/ec2/sweep.go +++ b/internal/service/ec2/sweep.go @@ -167,6 +167,7 @@ func init() { "aws_autoscaling_group", "aws_batch_compute_environment", "aws_elastic_beanstalk_environment", + "aws_cloud9_environment_ec2", "aws_cloudhsm_v2_cluster", "aws_db_subnet_group", "aws_directory_service_directory", From c6eba616812be75249e1a4e275ddfa8947d28d05 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 25 Nov 2021 14:08:39 +0200 Subject: [PATCH 3/7] changelog --- .changelog/18560.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18560.txt diff --git a/.changelog/18560.txt b/.changelog/18560.txt new file mode 100644 index 00000000000..d53210de2b0 --- /dev/null +++ b/.changelog/18560.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/cloud9_environment_ec2: Add plan time validations for `name`, `automatic_stop_time_minutes`, `description`. +``` \ No newline at end of file From 7237ccbbb6e816e75479cd6fc931fd3a07937819 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Thu, 25 Nov 2021 14:19:59 +0200 Subject: [PATCH 4/7] changelog --- internal/service/cloud9/find.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/cloud9/find.go b/internal/service/cloud9/find.go index d0bac992da0..4a1dd65aa54 100644 --- a/internal/service/cloud9/find.go +++ b/internal/service/cloud9/find.go @@ -27,7 +27,7 @@ func FindEnvironmentByID(conn *cloud9.Cloud9, id string) (*cloud9.Environment, e envs := out.Environments - if envs == nil || len(envs) == 0 { + if len(envs) == 0 { return nil, tfresource.NewEmptyResultError(input) } From 818f1bcd441d47d915494f0651247aa708f95ed8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 5 Jan 2022 10:10:30 -0500 Subject: [PATCH 5/7] r/aws_cloud9_ac2_environment: Fix sweeper. --- internal/service/cloud9/sweep.go | 49 ++++++++++++++------------------ internal/sweep/sweep_test.go | 1 + 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/internal/service/cloud9/sweep.go b/internal/service/cloud9/sweep.go index 5b7bc20c1ff..a60f64a2d4d 100644 --- a/internal/service/cloud9/sweep.go +++ b/internal/service/cloud9/sweep.go @@ -9,7 +9,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloud9" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/sweep" @@ -23,50 +22,44 @@ func init() { } func sweepEnvironmentEC2s(region string) error { - client, err := sharedClientForRegion(region) + client, err := sweep.SharedRegionalSweepClient(region) if err != nil { - return fmt.Errorf("error getting client: %w", err) + return fmt.Errorf("error getting client: %s", err) } - conn := client.(*AWSClient).cloud9conn - sweepResources := make([]*testSweepResource, 0) - var sweeperErrs *multierror.Error - + conn := client.(*conns.AWSClient).Cloud9Conn input := &cloud9.ListEnvironmentsInput{} + sweepResources := make([]*sweep.SweepResource, 0) + err = conn.ListEnvironmentsPages(input, func(page *cloud9.ListEnvironmentsOutput, lastPage bool) bool { - if len(page.EnvironmentIds) == 0 { - log.Printf("[INFO] No Cloud9 Environment EC2s to sweep") - return false + if page == nil { + return !lastPage } - for _, envID := range page.EnvironmentIds { - id := aws.StringValue(envID) - log.Printf("[INFO] Deleting Cloud9 Environment EC2: %s", id) + for _, v := range page.EnvironmentIds { r := ResourceEnvironmentEC2() d := r.Data(nil) - d.SetId(id) + d.SetId(aws.StringValue(v)) - if err != nil { - log.Printf("[ERROR] %s", err) - sweeperErrs = multierror.Append(sweeperErrs, err) - continue - } - sweepResources = append(sweepResources, NewTestSweepResource(r, d, client)) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + return !lastPage }) - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Cloud9 Environment EC2s: %w", err)) + if sweep.SkipSweepError(err) { + log.Printf("[WARN] Skipping Cloud9 EC2 Environment sweep for %s: %s", region, err) + return nil } - if err = testSweepResourceOrchestrator(sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping Cloud9 Environment EC2 for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing Cloud9 EC2 Environments (%s): %w", region, err) } - if testSweepSkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping Cloud9 Environment EC2s for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping Cloud9 EC2 Environments (%s): %w", region, err) } - return sweeperErrs.ErrorOrNil() + return nil } diff --git a/internal/sweep/sweep_test.go b/internal/sweep/sweep_test.go index b41ebdce0be..d113b173884 100644 --- a/internal/sweep/sweep_test.go +++ b/internal/sweep/sweep_test.go @@ -23,6 +23,7 @@ import ( _ "github.com/hashicorp/terraform-provider-aws/internal/service/backup" _ "github.com/hashicorp/terraform-provider-aws/internal/service/batch" _ "github.com/hashicorp/terraform-provider-aws/internal/service/budgets" + _ "github.com/hashicorp/terraform-provider-aws/internal/service/cloud9" _ "github.com/hashicorp/terraform-provider-aws/internal/service/cloudformation" _ "github.com/hashicorp/terraform-provider-aws/internal/service/cloudfront" _ "github.com/hashicorp/terraform-provider-aws/internal/service/cloudhsmv2" From 3c7bbce1e690f5aa394471fe000a68cce8459c6f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 5 Jan 2022 10:11:41 -0500 Subject: [PATCH 6/7] r/aws_cloud9_ac2_environment: Cosmetics. --- internal/service/cloud9/environment_ec2.go | 54 +++++++++++----------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/internal/service/cloud9/environment_ec2.go b/internal/service/cloud9/environment_ec2.go index 545d733e47a..ba5906d5821 100644 --- a/internal/service/cloud9/environment_ec2.go +++ b/internal/service/cloud9/environment_ec2.go @@ -83,30 +83,32 @@ func resourceEnvironmentEC2Create(d *schema.ResourceData, meta interface{}) erro defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - params := &cloud9.CreateEnvironmentEC2Input{ - InstanceType: aws.String(d.Get("instance_type").(string)), - Name: aws.String(d.Get("name").(string)), + name := d.Get("name").(string) + input := &cloud9.CreateEnvironmentEC2Input{ ClientRequestToken: aws.String(resource.UniqueId()), + InstanceType: aws.String(d.Get("instance_type").(string)), + Name: aws.String(name), Tags: Tags(tags.IgnoreAWS()), } if v, ok := d.GetOk("automatic_stop_time_minutes"); ok { - params.AutomaticStopTimeMinutes = aws.Int64(int64(v.(int))) + input.AutomaticStopTimeMinutes = aws.Int64(int64(v.(int))) } if v, ok := d.GetOk("description"); ok { - params.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } if v, ok := d.GetOk("owner_arn"); ok { - params.OwnerArn = aws.String(v.(string)) + input.OwnerArn = aws.String(v.(string)) } if v, ok := d.GetOk("subnet_id"); ok { - params.SubnetId = aws.String(v.(string)) + input.SubnetId = aws.String(v.(string)) } - var out *cloud9.CreateEnvironmentEC2Output + log.Printf("[INFO] Creating Cloud9 EC2 Environment: %s", input) + var output *cloud9.CreateEnvironmentEC2Output err := resource.Retry(tfiam.PropagationTimeout, func() *resource.RetryError { var err error - out, err = conn.CreateEnvironmentEC2(params) + output, err = conn.CreateEnvironmentEC2(input) if err != nil { // NotFoundException: User arn:aws:iam::*******:user/****** does not exist. if tfawserr.ErrMessageContains(err, cloud9.ErrCodeNotFoundException, "User") { @@ -116,18 +118,21 @@ func resourceEnvironmentEC2Create(d *schema.ResourceData, meta interface{}) erro } return nil }) + if tfresource.TimedOut(err) { - out, err = conn.CreateEnvironmentEC2(params) + output, err = conn.CreateEnvironmentEC2(input) } if err != nil { - return fmt.Errorf("Error creating Cloud9 EC2 Environment: %w", err) + return fmt.Errorf("error creating Cloud9 EC2 Environment (%s): %w", name, err) } - d.SetId(aws.StringValue(out.EnvironmentId)) + + d.SetId(aws.StringValue(output.EnvironmentId)) _, err = waitEnvironmentReady(conn, d.Id()) + if err != nil { - return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) to be ready: %w", d.Id(), err) + return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) create: %w", d.Id(), err) } return resourceEnvironmentEC2Read(d, meta) @@ -138,18 +143,16 @@ func resourceEnvironmentEC2Read(d *schema.ResourceData, meta interface{}) error defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - log.Printf("[INFO] Reading Cloud9 Environment EC2 %s", d.Id()) - env, err := FindEnvironmentByID(conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] Cloud9 Environment EC2 (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Cloud9 EC2 Environment (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error reading Cloud9 Environment EC2 (%s): %w", d.Id(), err) + return fmt.Errorf("error reading Cloud9 EC2 Environment (%s): %w", d.Id(), err) } arn := aws.StringValue(env.Arn) @@ -176,8 +179,6 @@ func resourceEnvironmentEC2Read(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("error setting tags_all: %w", err) } - log.Printf("[DEBUG] Received Cloud9 Environment EC2: %s", env) - return nil } @@ -191,14 +192,12 @@ func resourceEnvironmentEC2Update(d *schema.ResourceData, meta interface{}) erro Name: aws.String(d.Get("name").(string)), } - log.Printf("[INFO] Updating Cloud9 Environment EC2: %s", input) + log.Printf("[INFO] Updating Cloud9 EC2 Environment: %s", input) + _, err := conn.UpdateEnvironment(&input) - out, err := conn.UpdateEnvironment(&input) if err != nil { - return err + return fmt.Errorf("error updating Cloud9 EC2 Environment (%s): %w", d.Id(), err) } - - log.Printf("[DEBUG] Cloud9 Environment EC2 updated: %s", out) } if d.HasChange("tags_all") { @@ -216,20 +215,23 @@ func resourceEnvironmentEC2Update(d *schema.ResourceData, meta interface{}) erro func resourceEnvironmentEC2Delete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).Cloud9Conn + log.Printf("[INFO] Deleting Cloud9 EC2 Environment: %s", d.Id()) _, err := conn.DeleteEnvironment(&cloud9.DeleteEnvironmentInput{ EnvironmentId: aws.String(d.Id()), }) + if tfawserr.ErrCodeEquals(err, cloud9.ErrCodeNotFoundException) { return nil } if err != nil { - return err + return fmt.Errorf("error deleting Cloud9 EC2 Environment (%s): %w", d.Id(), err) } _, err = waitEnvironmentDeleted(conn, d.Id()) + if err != nil { - return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) to be deleted: %w", d.Id(), err) + return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) delete: %w", d.Id(), err) } return nil From 7dc2a236c745491d70ac65fc74eff02728a65fd0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 5 Jan 2022 10:20:31 -0500 Subject: [PATCH 7/7] Correct resource name in CHANGELOG entry. --- .changelog/18560.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/18560.txt b/.changelog/18560.txt index d53210de2b0..3b152e3bcda 100644 --- a/.changelog/18560.txt +++ b/.changelog/18560.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/cloud9_environment_ec2: Add plan time validations for `name`, `automatic_stop_time_minutes`, `description`. -``` \ No newline at end of file +resource/aws_cloud9_environment_ec2: Add plan time validations for `name`, `automatic_stop_time_minutes`, `description`. +```