Skip to content

Commit

Permalink
Merge pull request #21855 from DrFaust92/r/emr_studio
Browse files Browse the repository at this point in the history
r/emr studio - new resource
  • Loading branch information
ewbankkit committed Dec 7, 2021
2 parents 39568e2 + d51f391 commit 5fc93b3
Show file tree
Hide file tree
Showing 9 changed files with 775 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .changelog/21855.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_emr_studio
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@ func Provider() *schema.Provider {
"aws_emr_instance_group": emr.ResourceInstanceGroup(),
"aws_emr_managed_scaling_policy": emr.ResourceManagedScalingPolicy(),
"aws_emr_security_configuration": emr.ResourceSecurityConfiguration(),
"aws_emr_studio": emr.ResourceStudio(),

"aws_kinesis_firehose_delivery_stream": firehose.ResourceDeliveryStream(),

Expand Down
1 change: 1 addition & 0 deletions internal/service/ec2/sweep.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func init() {
"aws_elasticsearch_domain",
"aws_elb",
"aws_emr_cluster",
"aws_emr_studio",
"aws_fsx_lustre_file_system",
"aws_fsx_ontap_file_system",
"aws_fsx_windows_file_system",
Expand Down
25 changes: 25 additions & 0 deletions internal/service/emr/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,28 @@ func FindClusterByID(conn *emr.EMR, id string) (*emr.Cluster, error) {

return output, nil
}

func FindStudioByID(conn *emr.EMR, id string) (*emr.Studio, error) {
input := &emr.DescribeStudioInput{
StudioId: aws.String(id),
}

output, err := conn.DescribeStudio(input)

if tfawserr.ErrMessageContains(err, emr.ErrCodeInvalidRequestException, "Studio does not exist") {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil || output.Studio == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output.Studio, nil
}
27 changes: 16 additions & 11 deletions internal/service/emr/managed_scaling_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ import (
tfemr "github.com/hashicorp/terraform-provider-aws/internal/service/emr"
)

func init() {
acctest.RegisterServiceErrorCheckFunc(emr.EndpointsID, testAccErrorCheckSkipEMR)
}

func testAccErrorCheckSkipEMR(t *testing.T) resource.ErrorCheckFunc {
return acctest.ErrorCheckSkipMessagesContaining(t,
"Managed scaling is not available",
"SSO is not enabled",
"Account is not whitelisted to use this feature",
)
}

func TestAccEMRManagedScalingPolicy_basic(t *testing.T) {
resourceName := "aws_emr_managed_scaling_policy.testpolicy"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: testAccErrorCheckSkipEmrManagedScalingPolicy(t),
ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckManagedScalingPolicyDestroy,

Expand All @@ -45,7 +57,7 @@ func TestAccEMRManagedScalingPolicy_ComputeLimits_maximumCoreCapacityUnits(t *te
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: testAccErrorCheckSkipEmrManagedScalingPolicy(t),
ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckManagedScalingPolicyDestroy,

Expand All @@ -70,7 +82,7 @@ func TestAccEMRManagedScalingPolicy_ComputeLimits_maximumOnDemandCapacityUnits(t
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: testAccErrorCheckSkipEmrManagedScalingPolicy(t),
ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckManagedScalingPolicyDestroy,

Expand All @@ -95,7 +107,7 @@ func TestAccEMRManagedScalingPolicy_disappears(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: testAccErrorCheckSkipEmrManagedScalingPolicy(t),
ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckManagedScalingPolicyDestroy,
Steps: []resource.TestStep{
Expand All @@ -111,13 +123,6 @@ func TestAccEMRManagedScalingPolicy_disappears(t *testing.T) {
})
}

// testAccErrorCheckSkipEmrManagedScalingPolicy skips tests that have error messages indicating unsupported features
func testAccErrorCheckSkipEmrManagedScalingPolicy(t *testing.T) resource.ErrorCheckFunc {
return acctest.ErrorCheckSkipMessagesContaining(t,
"Managed scaling is not available",
)
}

func testAccManagedScalingPolicy_basic(r string) string {
return fmt.Sprintf(testAccManagedScalingPolicyBase+`
resource "aws_emr_managed_scaling_policy" "testpolicy" {
Expand Down
274 changes: 274 additions & 0 deletions internal/service/emr/studio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
package emr

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/emr"
"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"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

func ResourceStudio() *schema.Resource {
return &schema.Resource{
Create: resourceStudioCreate,
Read: resourceStudioRead,
Update: resourceStudioUpdate,
Delete: resourceStudioDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

CustomizeDiff: verify.SetTagsDiff,

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"auth_mode": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(emr.AuthMode_Values(), false),
},
"default_s3_location": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(0, 256),
},
"engine_security_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"idp_auth_url": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"idp_relay_state_parameter_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 256),
},
"service_role": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: verify.ValidARN,
},
"subnet_ids": {
Type: schema.TypeSet,
MaxItems: 5,
MinItems: 1,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
"user_role": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidARN,
},
"url": {
Type: schema.TypeString,
Computed: true,
},
"vpc_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"workspace_security_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceStudioCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EMRConn
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))

input := &emr.CreateStudioInput{
AuthMode: aws.String(d.Get("auth_mode").(string)),
DefaultS3Location: aws.String(d.Get("default_s3_location").(string)),
EngineSecurityGroupId: aws.String(d.Get("engine_security_group_id").(string)),
Name: aws.String(d.Get("name").(string)),
ServiceRole: aws.String(d.Get("service_role").(string)),
SubnetIds: flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set)),
VpcId: aws.String(d.Get("vpc_id").(string)),
WorkspaceSecurityGroupId: aws.String(d.Get("workspace_security_group_id").(string)),
}

if v, ok := d.GetOk("description"); ok {
input.Description = aws.String(v.(string))
}

if v, ok := d.GetOk("idp_auth_url"); ok {
input.IdpAuthUrl = aws.String(v.(string))
}

if v, ok := d.GetOk("idp_relay_state_parameter_name"); ok {
input.IdpRelayStateParameterName = aws.String(v.(string))
}

if v, ok := d.GetOk("user_role"); ok {
input.UserRole = aws.String(v.(string))
}

if len(tags) > 0 {
input.Tags = Tags(tags.IgnoreAWS())
}

var result *emr.CreateStudioOutput
err := resource.Retry(tfiam.PropagationTimeout, func() *resource.RetryError {
var err error
result, err = conn.CreateStudio(input)
if tfawserr.ErrMessageContains(err, emr.ErrCodeInvalidRequestException, "entity does not have permissions to assume role") {
return resource.RetryableError(err)
}
if tfawserr.ErrMessageContains(err, emr.ErrCodeInvalidRequestException, "Service role does not have permission to access") {
return resource.RetryableError(err)
}
if err != nil {
return resource.NonRetryableError(err)
}
return nil
})
if tfresource.TimedOut(err) {
result, err = conn.CreateStudio(input)
}
if err != nil {
return fmt.Errorf("error creating EMR Studio: %w", err)
}

d.SetId(aws.StringValue(result.StudioId))

return resourceStudioRead(d, meta)
}

func resourceStudioUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EMRConn

if d.HasChangesExcept("tags", "tags_all") {
input := &emr.UpdateStudioInput{
StudioId: aws.String(d.Id()),
}

if d.HasChange("description") {
input.Description = aws.String(d.Get("description").(string))
}

if d.HasChange("name") {
input.Name = aws.String(d.Get("name").(string))
}

if d.HasChange("default_s3_location") {
input.DefaultS3Location = aws.String(d.Get("default_s3_location").(string))
}

if d.HasChange("subnet_ids") {
input.SubnetIds = flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set))
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

if err := UpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating EMR Studio (%s) tags: %w", d.Id(), err)
}
}

return resourceStudioRead(d, meta)
}

func resourceStudioRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EMRConn
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

studio, err := FindStudioByID(conn, d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] EMR Studio (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading EMR Studio (%s): %w", d.Id(), err)
}

d.Set("arn", studio.StudioArn)
d.Set("auth_mode", studio.AuthMode)
d.Set("default_s3_location", studio.DefaultS3Location)
d.Set("description", studio.Description)
d.Set("engine_security_group_id", studio.EngineSecurityGroupId)
d.Set("idp_auth_url", studio.IdpAuthUrl)
d.Set("idp_relay_state_parameter_name", studio.IdpRelayStateParameterName)
d.Set("name", studio.Name)
d.Set("service_role", studio.ServiceRole)
d.Set("url", studio.Url)
d.Set("user_role", studio.UserRole)
d.Set("vpc_id", studio.VpcId)
d.Set("workspace_security_group_id", studio.WorkspaceSecurityGroupId)
d.Set("subnet_ids", flex.FlattenStringSet(studio.SubnetIds))

tags := KeyValueTags(studio.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

return nil
}

func resourceStudioDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EMRConn

request := &emr.DeleteStudioInput{
StudioId: aws.String(d.Id()),
}

log.Printf("[INFO] Deleting EMR Studio: %s", d.Id())
_, err := conn.DeleteStudio(request)

if err != nil {
if tfawserr.ErrCodeEquals(err, emr.ErrCodeInternalServerException) {
return nil
}
return fmt.Errorf("error deleting EMR Studio (%s): %w", d.Id(), err)
}

return nil
}
Loading

0 comments on commit 5fc93b3

Please sign in to comment.