Skip to content

Commit

Permalink
Merge pull request #18560 from DrFaust92/cloud9_refactor_waiters
Browse files Browse the repository at this point in the history
r/cloud9_environment_ec2 - validations + refactor to use finder and waiter packages + sweeper
  • Loading branch information
ewbankkit authored Jan 5, 2022
2 parents 7b58ccc + 7dc2a23 commit 220f03d
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 181 deletions.
3 changes: 3 additions & 0 deletions .changelog/18560.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_cloud9_environment_ec2: Add plan time validations for `name`, `automatic_stop_time_minutes`, `description`.
```
167 changes: 59 additions & 108 deletions internal/service/cloud9/environment_ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -30,22 +30,25 @@ 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,
Required: true,
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,
Expand Down Expand Up @@ -80,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") {
Expand All @@ -113,44 +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: %s", err)
return fmt.Errorf("error creating Cloud9 EC2 Environment (%s): %w", name, 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
}
d.SetId(aws.StringValue(output.EnvironmentId))

status := aws.StringValue(out.Status)
_, err = waitEnvironmentReady(conn, d.Id())

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()
if err != nil {
return err
return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) create: %w", d.Id(), err)
}

return resourceEnvironmentEC2Read(d, meta)
Expand All @@ -161,25 +143,17 @@ 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())

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 {
log.Printf("[WARN] Cloud9 Environment EC2 (%s) not found, removing from state", d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Cloud9 EC2 Environment (%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 EC2 Environment (%s): %w", d.Id(), err)
}

arn := aws.StringValue(env.Arn)
d.Set("arn", arn)
Expand All @@ -191,7 +165,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)
Expand All @@ -205,35 +179,33 @@ 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
}

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 EC2 Environment: %s", input)
_, err := conn.UpdateEnvironment(&input)

out, err := conn.UpdateEnvironment(&input)
if err != nil {
return err
if err != nil {
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") {
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)
}
}

Expand All @@ -243,45 +215,24 @@ 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 err != nil {
return err
}

input := &cloud9.DescribeEnvironmentsInput{
EnvironmentIds: []*string{aws.String(d.Id())},
if tfawserr.ErrCodeEquals(err, cloud9.ErrCodeNotFoundException) {
return nil
}
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
}

if err != nil {
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 deleting Cloud9 EC2 Environment: %s", err)
return fmt.Errorf("error waiting for Cloud9 EC2 Environment (%s) delete: %w", d.Id(), err)
}

return nil
}
Loading

0 comments on commit 220f03d

Please sign in to comment.