Skip to content

Commit

Permalink
feat: Add unhealthy_node_replacement arg for aws_emr_cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
acwwat committed Mar 22, 2024
1 parent 691f4c1 commit 34f9c41
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/36523.txt
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_emr_cluster: Add `unhealthy_node_replacement` argument
```
27 changes: 25 additions & 2 deletions internal/service/emr/cluster.go
Expand Up @@ -569,18 +569,23 @@ func ResourceCluster() *schema.Resource {
Default: 1,
ValidateFunc: validation.IntBetween(1, 256),
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"termination_protection": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"unhealthy_node_replacement": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"visible_to_all_users": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
},
}
}
Expand Down Expand Up @@ -784,9 +789,16 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
if v, ok := d.GetOk("termination_protection"); ok {
terminationProtection = v.(bool)
}

unhealthyNodeReplacement := false
if v, ok := d.GetOk("unhealthy_node_replacement"); ok {
unhealthyNodeReplacement = v.(bool)
}

instanceConfig := &emr.JobFlowInstancesConfig{
KeepJobFlowAliveWhenNoSteps: aws.Bool(keepJobFlowAliveWhenNoSteps),
TerminationProtected: aws.Bool(terminationProtection),
UnhealthyNodeReplacement: aws.Bool(unhealthyNodeReplacement),
}

if l := d.Get("master_instance_group").([]interface{}); len(l) > 0 && l[0] != nil {
Expand Down Expand Up @@ -1116,6 +1128,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter
d.Set("ebs_root_volume_size", cluster.EbsRootVolumeSize)
d.Set("scale_down_behavior", cluster.ScaleDownBehavior)
d.Set("termination_protection", cluster.TerminationProtected)
d.Set("unhealthy_node_replacement", cluster.UnhealthyNodeReplacement)
d.Set("step_concurrency_level", cluster.StepConcurrencyLevel)

d.Set("custom_ami_id", cluster.CustomAmiId)
Expand Down Expand Up @@ -1262,6 +1275,16 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int
}
}

if d.HasChange("unhealthy_node_replacement") {
_, err := conn.SetUnhealthyNodeReplacementWithContext(ctx, &emr.SetUnhealthyNodeReplacementInput{
JobFlowIds: []*string{aws.String(d.Id())},
UnhealthyNodeReplacement: aws.Bool(d.Get("unhealthy_node_replacement").(bool)),
})
if err != nil {
return sdkdiag.AppendErrorf(diags, "updating EMR Cluster (%s): setting unhealthy node replacement: %s", d.Id(), err)
}
}

if d.HasChange("core_instance_group.0.autoscaling_policy") {
autoscalingPolicyStr := d.Get("core_instance_group.0.autoscaling_policy").(string)
instanceGroupID := d.Get("core_instance_group.0.id").(string)
Expand Down
81 changes: 81 additions & 0 deletions internal/service/emr/cluster_test.go
Expand Up @@ -1702,6 +1702,56 @@ func TestAccEMRCluster_InstanceFleetMaster_only(t *testing.T) {
})
}

func TestAccEMRCluster_unhealthyNodeReplacement(t *testing.T) {
ctx := acctest.Context(t)
var cluster emr.Cluster

resourceName := "aws_emr_cluster.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EMRServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckClusterDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccClusterConfig_unhealthyNodeReplacement(rName, "true"),
Check: resource.ComposeTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &cluster),
resource.TestCheckResourceAttr(resourceName, "unhealthy_node_replacement", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"cluster_state", // Ignore RUNNING versus WAITING changes
"configurations",
"keep_job_flow_alive_when_no_steps",
},
},
{
Config: testAccClusterConfig_unhealthyNodeReplacement(rName, "false"),
Check: resource.ComposeTestCheckFunc(
testAccCheckClusterExists(ctx, resourceName, &cluster),
resource.TestCheckResourceAttr(resourceName, "unhealthy_node_replacement", "false"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"cluster_state", // Ignore RUNNING versus WAITING changes
"configurations",
"keep_job_flow_alive_when_no_steps",
},
},
},
})
}

func testAccCheckClusterDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn(ctx)
Expand Down Expand Up @@ -4292,3 +4342,34 @@ resource "aws_emr_cluster" "test" {
}
`, rName))
}

func testAccClusterConfig_unhealthyNodeReplacement(rName, unhealthyNodeReplacement string) string {
return acctest.ConfigCompose(
testAccClusterConfig_baseVPC(rName, false),
fmt.Sprintf(`
data "aws_partition" "current" {}
resource "aws_emr_cluster" "test" {
applications = ["Spark"]
keep_job_flow_alive_when_no_steps = true
name = %[1]q
release_label = "emr-5.33.1"
service_role = "EMR_DefaultRole"
termination_protection = false
unhealthy_node_replacement = %[2]s
ec2_attributes {
instance_profile = "EMR_EC2_DefaultRole"
subnet_id = aws_subnet.test.id
emr_managed_master_security_group = aws_security_group.test.id
emr_managed_slave_security_group = aws_security_group.test.id
}
master_instance_group {
instance_type = "m4.large"
}
depends_on = [aws_route_table_association.test]
}
`, rName, unhealthyNodeReplacement))
}
1 change: 1 addition & 0 deletions website/docs/r/emr_cluster.html.markdown
Expand Up @@ -663,6 +663,7 @@ EOF
* `step_concurrency_level` - (Optional) Number of steps that can be executed concurrently. You can specify a maximum of 256 steps. Only valid for EMR clusters with `release_label` 5.28.0 or greater (default is 1).
* `tags` - (Optional) list of tags to apply to the EMR Cluster. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
* `termination_protection` - (Optional) Switch on/off termination protection (default is `false`, except when using multiple master nodes). Before attempting to destroy the resource when termination protection is enabled, this configuration must be applied with its value set to `false`.
* `unhealthy_node_replacement` - (Optional) Whether whether Amazon EMR should gracefully replace core nodes that have degraded within the cluster. Default value is `false`.
* `visible_to_all_users` - (Optional) Whether the job flow is visible to all IAM users of the AWS account associated with the job flow. Default value is `true`.

### bootstrap_action
Expand Down

0 comments on commit 34f9c41

Please sign in to comment.