Skip to content

Commit

Permalink
Merge pull request #17219 from kristerjaanhold/f-resource_aws_emr_clu…
Browse files Browse the repository at this point in the history
…ster
  • Loading branch information
gdavison committed Apr 15, 2021
2 parents 88c8d07 + 9b49326 commit 228db50
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .changelog/17219.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_emr_cluster: Adds support for multiple subnets
```
23 changes: 20 additions & 3 deletions aws/resource_aws_emr_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,20 @@ func resourceAwsEMRCluster() *schema.Resource {
ForceNew: true,
},
"subnet_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ConflictsWith: []string{"ec2_attributes.0.subnet_ids"},
},
"subnet_ids": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
ConflictsWith: []string{"ec2_attributes.0.subnet_id"},
},
"additional_master_security_groups": {
Type: schema.TypeString,
Expand Down Expand Up @@ -783,6 +794,9 @@ func resourceAwsEMRClusterCreate(d *schema.ResourceData, meta interface{}) error
if v, ok := attributes["subnet_id"]; ok {
instanceConfig.Ec2SubnetId = aws.String(v.(string))
}
if v, ok := attributes["subnet_ids"]; ok {
instanceConfig.Ec2SubnetIds = expandStringSet(v.(*schema.Set))
}

if v, ok := attributes["additional_master_security_groups"]; ok {
strSlice := strings.Split(v.(string), ",")
Expand Down Expand Up @@ -1445,6 +1459,9 @@ func flattenEc2Attributes(ia *emr.Ec2InstanceAttributes) []map[string]interface{
if ia.Ec2SubnetId != nil {
attrs["subnet_id"] = *ia.Ec2SubnetId
}
if ia.RequestedEc2SubnetIds != nil && len(ia.RequestedEc2SubnetIds) > 0 {
attrs["subnet_ids"] = flattenStringSet(ia.RequestedEc2SubnetIds)
}
if ia.IamInstanceProfile != nil {
attrs["instance_profile"] = *ia.IamInstanceProfile
}
Expand Down
168 changes: 161 additions & 7 deletions aws/resource_aws_emr_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1387,30 +1387,74 @@ func TestAccAWSEMRCluster_custom_ami_id(t *testing.T) {
})
}

func TestAccAWSEMRCluster_instance_fleet(t *testing.T) {
var cluster emr.Cluster
func TestAccAWSEMRCluster_InstanceFleet_basic(t *testing.T) {
var cluster1, cluster2 emr.Cluster

resourceName := "aws_emr_cluster.tf-test-cluster"
subnetResourceName := "aws_subnet.test"
subnet2ResourceName := "aws_subnet.test2"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, emr.EndpointsID),
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEmrDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEmrClusterConfigInstanceFleets(rName),
Config: testAccAWSEmrClusterConfig_InstanceFleets(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEmrClusterExists(resourceName, &cluster),
testAccCheckAWSEmrClusterExists(resourceName, &cluster1),
resource.TestCheckResourceAttr(resourceName, "master_instance_fleet.#", "1"),
resource.TestCheckResourceAttr(resourceName, "core_instance_fleet.#", "1"),
resource.TestCheckResourceAttr(resourceName, "ec2_attributes.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "ec2_attributes.0.subnet_id", subnetResourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "ec2_attributes.0.subnet_ids.#", "1"),
resource.TestCheckTypeSetElemAttrPair(resourceName, "ec2_attributes.0.subnet_ids.*", subnetResourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "master_instance_group.#", "0"),
resource.TestCheckResourceAttr(resourceName, "core_instance_group.#", "0"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"cluster_state", // Ignore RUNNING versus WAITING changes
"configurations",
"keep_job_flow_alive_when_no_steps",
},
},
{
Config: testAccAWSEmrClusterConfig_InstanceFleet_MultipleSubnets(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEmrClusterExists(resourceName, &cluster2),
testAccCheckAWSEmrClusterRecreated(&cluster1, &cluster2),
resource.TestCheckResourceAttr(resourceName, "master_instance_fleet.#", "1"),
resource.TestCheckResourceAttr(resourceName, "core_instance_fleet.#", "1"),
resource.TestCheckResourceAttr(resourceName, "ec2_attributes.#", "1"),
resource.TestCheckResourceAttr(resourceName, "ec2_attributes.0.subnet_ids.#", "2"),
resource.TestCheckTypeSetElemAttrPair(resourceName, "ec2_attributes.0.subnet_ids.*", subnetResourceName, "id"),
resource.TestCheckTypeSetElemAttrPair(resourceName, "ec2_attributes.0.subnet_ids.*", subnet2ResourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "master_instance_group.#", "0"),
resource.TestCheckResourceAttr(resourceName, "core_instance_group.#", "0"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"cluster_state", // Ignore RUNNING versus WAITING changes
"configurations",
"keep_job_flow_alive_when_no_steps",
},
},
},
})
}

func TestAccAWSEMRCluster_instance_fleet_master_only(t *testing.T) {
func TestAccAWSEMRCluster_InstanceFleet_master_only(t *testing.T) {
var cluster emr.Cluster

resourceName := "aws_emr_cluster.tf-test-cluster"
Expand All @@ -1426,8 +1470,19 @@ func TestAccAWSEMRCluster_instance_fleet_master_only(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEmrClusterExists(resourceName, &cluster),
resource.TestCheckResourceAttr(resourceName, "master_instance_fleet.#", "1"),
resource.TestCheckResourceAttr(resourceName, "core_instance_fleet.#", "0"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"cluster_state", // Ignore RUNNING versus WAITING changes
"configurations",
"keep_job_flow_alive_when_no_steps",
},
},
},
})
}
Expand Down Expand Up @@ -3185,7 +3240,7 @@ resource "aws_security_group" "test" {
resource "aws_subnet" "test" {
availability_zone = data.aws_availability_zones.available.names[0]
cidr_block = "10.0.0.0/24"
cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0)
map_public_ip_on_launch = %[1]t
vpc_id = aws_vpc.test.id
Expand Down Expand Up @@ -3503,7 +3558,7 @@ resource "aws_emr_cluster" "tf-test-cluster" {
)
}

func testAccAWSEmrClusterConfigInstanceFleets(r string) string {
func testAccAWSEmrClusterConfig_InstanceFleets(r string) string {
return testAccAWSEmrComposeConfig(false,
testAccAWSEmrClusterConfigCurrentPartition(),
testAccAWSEmrClusterConfigIAMServiceRoleBase(r),
Expand Down Expand Up @@ -3590,6 +3645,105 @@ resource "aws_emr_cluster" "tf-test-cluster" {
)
}

func testAccAWSEmrClusterConfig_InstanceFleet_MultipleSubnets(r string) string {
return testAccAWSEmrComposeConfig(false,
testAccAWSEmrClusterConfigCurrentPartition(),
testAccAWSEmrClusterConfigIAMServiceRoleBase(r),
testAccAWSEmrClusterConfigIAMInstanceProfileBase(r),
testAccAWSEmrClusterConfigBootstrapActionBucket(r),
fmt.Sprintf(`
resource "aws_emr_cluster" "tf-test-cluster" {
name = "%[1]s"
release_label = "emr-5.30.1"
applications = ["Hadoop", "Hive"]
log_uri = "s3n://terraform/testlog/"
master_instance_fleet {
instance_type_configs {
instance_type = "m3.xlarge"
}
target_on_demand_capacity = 1
}
core_instance_fleet {
instance_type_configs {
bid_price_as_percentage_of_on_demand_price = 80
ebs_config {
size = 100
type = "gp2"
volumes_per_instance = 1
}
instance_type = "m3.xlarge"
weighted_capacity = 1
}
instance_type_configs {
bid_price_as_percentage_of_on_demand_price = 100
ebs_config {
size = 100
type = "gp2"
volumes_per_instance = 1
}
instance_type = "m4.xlarge"
weighted_capacity = 1
}
instance_type_configs {
bid_price_as_percentage_of_on_demand_price = 100
ebs_config {
size = 100
type = "gp2"
volumes_per_instance = 1
}
instance_type = "m4.2xlarge"
weighted_capacity = 2
}
launch_specifications {
spot_specification {
allocation_strategy = "capacity-optimized"
block_duration_minutes = 0
timeout_action = "SWITCH_TO_ON_DEMAND"
timeout_duration_minutes = 10
}
}
name = "core fleet"
target_on_demand_capacity = 0
target_spot_capacity = 2
}
service_role = aws_iam_role.emr_service.arn
depends_on = [
aws_route_table_association.test,
aws_iam_role_policy_attachment.emr_service,
aws_iam_role_policy_attachment.emr_instance_profile,
]
ec2_attributes {
subnet_ids = [aws_subnet.test.id,aws_subnet.test2.id]
emr_managed_master_security_group = aws_security_group.test.id
emr_managed_slave_security_group = aws_security_group.test.id
instance_profile = aws_iam_instance_profile.emr_instance_profile.arn
}
bootstrap_action {
path = "s3://elasticmapreduce/bootstrap-actions/run-if"
name = "runif"
args = ["instance.isMaster=true", "echo running on master node"]
}
}
resource "aws_subnet" "test2" {
availability_zone = data.aws_availability_zones.available.names[1]
cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 1)
map_public_ip_on_launch = false
vpc_id = aws_vpc.test.id
}
resource "aws_route_table_association" "test2" {
route_table_id = aws_route_table.test.id
subnet_id = aws_subnet.test2.id
}
`, r),
)
}

func testAccAWSEmrClusterConfigInstanceFleetsMasterOnly(r string) string {
return testAccAWSEmrComposeConfig(false,
testAccAWSEmrClusterConfigCurrentPartition(),
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/emr_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ Attributes for the Amazon EC2 instances running the job flow

* `key_name` - (Optional) Amazon EC2 key pair that can be used to ssh to the master node as the user called `hadoop`
* `subnet_id` - (Optional) VPC subnet id where you want the job flow to launch. Cannot specify the `cc1.4xlarge` instance type for nodes of a job flow launched in a Amazon VPC
* `subnet_ids` - (Optional) List of VPC subnet id-s where you want the job flow to launch. Amazon EMR identifies the best Availability Zone to launch instances according to your fleet specifications
* `additional_master_security_groups` - (Optional) String containing a comma separated list of additional Amazon EC2 security group IDs for the master node
* `additional_slave_security_groups` - (Optional) String containing a comma separated list of additional Amazon EC2 security group IDs for the slave nodes as a comma separated string
* `emr_managed_master_security_group` - (Optional) Identifier of the Amazon EC2 EMR-Managed security group for the master node
Expand Down

0 comments on commit 228db50

Please sign in to comment.