Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 13952 #13965

Merged
merged 20 commits into from
Aug 25, 2020
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ func Provider() *schema.Provider {
"aws_elb_attachment": resourceAwsElbAttachment(),
"aws_emr_cluster": resourceAwsEMRCluster(),
"aws_emr_instance_group": resourceAwsEMRInstanceGroup(),
"aws_emr_managed_scaling_policy": resourceAwsEMRManagedScalingPolicy(),
"aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(),
"aws_flow_log": resourceAwsFlowLog(),
"aws_fsx_lustre_file_system": resourceAwsFsxLustreFileSystem(),
Expand Down
142 changes: 142 additions & 0 deletions aws/resource_aws_emr_managed_scaling_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package aws

import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/emr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"log"
)

func resourceAwsEMRManagedScalingPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsEMRManagedScalingPolicyCreate,
Read: resourceAwsEMRManagedScalingPolicyRead,
Delete: resourceAwsEMRManagedScalingPolicyDelete,
Importer: &schema.ResourceImporter{
bflad marked this conversation as resolved.
Show resolved Hide resolved
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"cluster_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"compute_limits": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: computeLimitsSchema(),
c4po marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
}
func computeLimitsSchema() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"unit_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(emr.ComputeLimitsUnitType_Values(), false),
},
"minimum_capacity_units": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"maximum_capacity_units": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"maximum_core_capacity_units": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
"maximum_ondemand_capacity_units": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
},
}
}

func resourceAwsEMRManagedScalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).emrconn

if l := d.Get("compute_limits").(*schema.Set).List(); len(l) > 0 && l[0] != nil {
cl := l[0].(map[string]interface{})
computeLimits := &emr.ComputeLimits{
UnitType: aws.String(cl["unit_type"].(string)),
MinimumCapacityUnits: aws.Int64(int64(cl["minimum_capacity_units"].(int))),
MaximumCapacityUnits: aws.Int64(int64(cl["maximum_capacity_units"].(int))),
MaximumCoreCapacityUnits: aws.Int64(int64(cl["maximum_core_capacity_units"].(int))),
MaximumOnDemandCapacityUnits: aws.Int64(int64(cl["maximum_ondemand_capacity_units"].(int))),
c4po marked this conversation as resolved.
Show resolved Hide resolved
}
managedScalingPolicy := &emr.ManagedScalingPolicy{
ComputeLimits: computeLimits,
}

_, err := conn.PutManagedScalingPolicy(&emr.PutManagedScalingPolicyInput{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly does not need to be for this pull request, but since this API uses a PUT-type call, it should allow the resource to support an Update function (essentially similar to this Create function, except no d.SetId(), then removing all ForceNew from the compute_limits attributes). May be worth creating a followup GitHub issue if it is not implemented since the forced recreation may be a little confusing for operators.

ClusterId: aws.String(d.Get("cluster_id").(string)),
ManagedScalingPolicy: managedScalingPolicy,
})

if err != nil {
log.Printf("[ERROR] EMR.PutManagedScalingPolicy %s", err)
return fmt.Errorf("error putting EMR Managed Scaling Policy: %w", err)
}
}

d.SetId(d.Get("cluster_id").(string))
return nil
}

func resourceAwsEMRManagedScalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).emrconn
resp, err := conn.GetManagedScalingPolicy(&emr.GetManagedScalingPolicyInput{
ClusterId: aws.String(d.Id()),
})
if err != nil {
if isAWSErr(err, "InvalidRequestException", "does not exist") {
log.Printf("[WARN] EMR Managed Scaling Policy (%s) not found, removing from state", d.Get("cluster_id").(string))
d.SetId("")
return nil
}
return fmt.Errorf("error getting EMR Managed Scaling Policy (%s): %w", d.Id(), err)
}

if resp.ManagedScalingPolicy != nil {
attrs := make(map[string]interface{})
attrs["unit_type"] = aws.StringValue(resp.ManagedScalingPolicy.ComputeLimits.UnitType)
attrs["minimum_capacity_units"] = aws.Int64Value(resp.ManagedScalingPolicy.ComputeLimits.MinimumCapacityUnits)
attrs["maximum_capacity_units"] = aws.Int64Value(resp.ManagedScalingPolicy.ComputeLimits.MaximumCapacityUnits)
attrs["maximum_core_capacity_units"] = aws.Int64Value(resp.ManagedScalingPolicy.ComputeLimits.MaximumCoreCapacityUnits)
attrs["maximum_ondemand_capacity_units"] = aws.Int64Value(resp.ManagedScalingPolicy.ComputeLimits.MaximumOnDemandCapacityUnits)

computeLimits := make([]interface{}, 0)
computeLimits = append(computeLimits, attrs)
d.Set("compute_limits", computeLimits)
}

return nil
}

func resourceAwsEMRManagedScalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).emrconn
_, err := conn.RemoveManagedScalingPolicy(&emr.RemoveManagedScalingPolicyInput{
ClusterId: aws.String(d.Get("cluster_id").(string)),
})
if err != nil {
if isAWSErr(err, "InvalidRequestException", "does not exist") {
return nil
}
return fmt.Errorf("error removing EMR Managed Scaling Policy (%s): %w", d.Id(), err)
}
return nil
}
107 changes: 107 additions & 0 deletions aws/resource_aws_emr_managed_scaling_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package aws
bflad marked this conversation as resolved.
Show resolved Hide resolved

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/emr"
"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"
)

func TestAccAwsEmrManagedScalingPolicy_basic(t *testing.T) {
resourceName := "aws_emr_managed_scaling_policy.testpolicy"
rInt := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEmrManagedScalingPolicyDestroy,

Steps: []resource.TestStep{
{
Config: testAccAWSEmrManagedScalingPolicy_basic(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEmrManagedScalingPolicyExists(resourceName),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

c4po marked this conversation as resolved.
Show resolved Hide resolved
func testAccAWSEmrManagedScalingPolicy_basic(r int) string {
return fmt.Sprintf(testAccAWSEmrInstanceGroupBase+`
c4po marked this conversation as resolved.
Show resolved Hide resolved
resource "aws_emr_managed_scaling_policy" "testpolicy" {
cluster_id = aws_emr_cluster.tf-test-cluster.id
compute_limits {
unit_type = "Instances"
minimum_capacity_units = 1
maximum_capacity_units = 2
maximum_ondemand_capacity_units = 2
maximum_core_capacity_units = 2
c4po marked this conversation as resolved.
Show resolved Hide resolved
}
}
`, r)
}

func testAccCheckAWSEmrManagedScalingPolicyExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No EMR Managed Scaling Policy ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).emrconn
resp, err := conn.GetManagedScalingPolicy(&emr.GetManagedScalingPolicyInput{
ClusterId: aws.String(rs.Primary.Attributes["cluster_id"]),
c4po marked this conversation as resolved.
Show resolved Hide resolved
})
if err != nil {
return err
}

if resp.ManagedScalingPolicy == nil {
return fmt.Errorf("EMR Managed Scaling Policy is empty which shouldn't happen")
}
return nil
}
}

func testAccCheckAWSEmrManagedScalingPolicyDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).emrconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_emr_managed_scaling_policy" {
continue
}

resp, err := conn.DescribeSecurityConfiguration(&emr.DescribeSecurityConfigurationInput{
c4po marked this conversation as resolved.
Show resolved Hide resolved
Name: aws.String(rs.Primary.ID),
})

if isAWSErr(err, "InvalidRequestException", "does not exist") {
return nil
}

if err != nil {
return err
}

if resp != nil {
return fmt.Errorf("Error: EMR Managed Scaling Policy still exists")
}

return nil
}

return nil
}
58 changes: 58 additions & 0 deletions website/docs/r/emr_managed_scaling_policy.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
subcategory: "Elastic Map Reduce (EMR)"
layout: "aws"
page_title: "AWS: aws_emr_managed_scaling_policy"
description: |-
Provides a resource for EMR Managed Scaling policy
---

# Resource: aws_emr_managed_scaling_policy

Provides a Managed Scaling policy for EMR Cluster. With Amazon EMR versions 5.30.0 and later (except for Amazon EMR 6.0.0), you can enable EMR managed scaling to automatically increase or decrease the number of instances or units in your cluster based on workload. See [Using EMR Managed Scaling in Amazon EMR](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-managed-scaling.html) for more information.

## Example Usage

```hcl
resource "aws_emr_cluster" "sample" {
name = "emr-sample-cluster"
release_label = "emr-5.30.0"

master_instance_group {
instance_type = "m4.large"
}

core_instance_group {
instance_type = "c4.large"
}
# skip ...
}

resource "aws_emr_managed_scaling_policy" "samplepolicy" {
cluster_id = aws_emr_cluster.sample.id
compute_limits {
unit_type = "Instances"
minimum_capacity_units = 2
maximum_capacity_units = 10
maximum_ondemand_capacity_units = 2
maximum_core_capacity_units = 10
}
}
```

## Argument Reference

The following arguments are supported:

* `cluster_id` - (Required) The id of the EMR cluster
* `unit_type` - (Required) The unit type used for specifying a managed scaling policy. Valid Values: `InstanceFleetUnits` | `Instances` | `VCPU`
c4po marked this conversation as resolved.
Show resolved Hide resolved
* `minimum_capacity_units` - (Required) The lower boundary of EC2 units. It is measured through VCPU cores or instances for instance groups and measured through units for instance fleets. Managed scaling activities are not allowed beyond this boundary. The limit only applies to the core and task nodes. The master node cannot be scaled after initial configuration.
* `maximum_capacity_units` - (Required) The upper boundary of EC2 units. It is measured through VCPU cores or instances for instance groups and measured through units for instance fleets. Managed scaling activities are not allowed beyond this boundary. The limit only applies to the core and task nodes. The master node cannot be scaled after initial configuration.
* `maximum_ondemand_capacity_units` - (Optional) The upper boundary of On-Demand EC2 units. It is measured through VCPU cores or instances for instance groups and measured through units for instance fleets. The On-Demand units are not allowed to scale beyond this boundary. The parameter is used to split capacity allocation between On-Demand and Spot instances.
* `maximum_core_capacity_units` - (Optional) The upper boundary of EC2 units for core node type in a cluster. It is measured through VCPU cores or instances for instance groups and measured through units for instance fleets. The core units are not allowed to scale beyond this boundary. The parameter is used to split capacity allocation between core and task nodes.

## Import

EMR Managed Scaling Policies can be imported via the EMR Cluster identifier, e.g.
```console
$ terraform import aws_emr_managed_scaling_policy.example j-123456ABCDEF
```