From 904adeb3f4d385d35c7de292aad92cbdaa9164ee Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Wed, 11 Nov 2020 15:42:20 -0500 Subject: [PATCH] resource/aws_eks_node_group: Add capacity_type argument and support multiple instance_types Output from acceptance testing (see https://github.com/hashicorp/terraform-provider-aws/issues/16146 for unrelated failure triage on the main branch): ``` --- FAIL: TestAccAWSEksNodeGroup_LaunchTemplate_Version (2588.15s) --- FAIL: TestAccAWSEksNodeGroup_ReleaseVersion (4116.90s) --- PASS: TestAccAWSEksNodeGroup_AmiType (1739.66s) --- PASS: TestAccAWSEksNodeGroup_basic (1263.54s) --- PASS: TestAccAWSEksNodeGroup_CapacityType_Spot (1397.33s) --- PASS: TestAccAWSEksNodeGroup_disappears (1359.08s) --- PASS: TestAccAWSEksNodeGroup_DiskSize (1284.23s) --- PASS: TestAccAWSEksNodeGroup_ForceUpdateVersion (5320.15s) --- PASS: TestAccAWSEksNodeGroup_InstanceTypes_Multiple (1232.97s) --- PASS: TestAccAWSEksNodeGroup_InstanceTypes_Single (1490.11s) --- PASS: TestAccAWSEksNodeGroup_Labels (1268.53s) --- PASS: TestAccAWSEksNodeGroup_LaunchTemplate_Id (1711.11s) --- PASS: TestAccAWSEksNodeGroup_LaunchTemplate_Name (1661.18s) --- PASS: TestAccAWSEksNodeGroup_RemoteAccess_Ec2SshKey (1292.95s) --- PASS: TestAccAWSEksNodeGroup_RemoteAccess_SourceSecurityGroupIds (1306.54s) --- PASS: TestAccAWSEksNodeGroup_ScalingConfig_DesiredSize (1189.50s) --- PASS: TestAccAWSEksNodeGroup_ScalingConfig_MaxSize (1358.88s) --- PASS: TestAccAWSEksNodeGroup_ScalingConfig_MinSize (1428.47s) --- PASS: TestAccAWSEksNodeGroup_Tags (1366.22s) --- PASS: TestAccAWSEksNodeGroup_Version (5252.66s) ``` --- aws/resource_aws_eks_node_group.go | 15 ++- aws/resource_aws_eks_node_group_test.go | 134 +++++++++++++++++++- website/docs/r/eks_node_group.html.markdown | 3 +- 3 files changed, 142 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_eks_node_group.go b/aws/resource_aws_eks_node_group.go index 8d0c6074e4d..a3c83a9c770 100644 --- a/aws/resource_aws_eks_node_group.go +++ b/aws/resource_aws_eks_node_group.go @@ -47,6 +47,13 @@ func resourceAwsEksNodeGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "capacity_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: eks.CapacityTypesOnDemand, + ValidateFunc: validation.StringInSlice(eks.CapacityTypes_Values(), false), + }, "cluster_name": { Type: schema.TypeString, Required: true, @@ -68,9 +75,6 @@ func resourceAwsEksNodeGroup() *schema.Resource { Optional: true, Computed: true, ForceNew: true, - // Multiple instance types returns an API error currently: - // InvalidParameterException: Instance type list not valid, only one instance type is supported! - MaxItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, }, "labels": { @@ -232,6 +236,10 @@ func resourceAwsEksNodeGroupCreate(d *schema.ResourceData, meta interface{}) err input.AmiType = aws.String(v.(string)) } + if v, ok := d.GetOk("capacity_type"); ok { + input.CapacityType = aws.String(v.(string)) + } + if v, ok := d.GetOk("disk_size"); ok { input.DiskSize = aws.Int64(int64(v.(int))) } @@ -327,6 +335,7 @@ func resourceAwsEksNodeGroupRead(d *schema.ResourceData, meta interface{}) error d.Set("ami_type", nodeGroup.AmiType) d.Set("arn", nodeGroup.NodegroupArn) + d.Set("capacity_type", nodeGroup.CapacityType) d.Set("cluster_name", nodeGroup.ClusterName) d.Set("disk_size", nodeGroup.DiskSize) diff --git a/aws/resource_aws_eks_node_group_test.go b/aws/resource_aws_eks_node_group_test.go index 83400f65952..f1391b4b9de 100644 --- a/aws/resource_aws_eks_node_group_test.go +++ b/aws/resource_aws_eks_node_group_test.go @@ -96,6 +96,7 @@ func TestAccAWSEksNodeGroup_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "ami_type", eks.AMITypesAl2X8664), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("nodegroup/%[1]s/%[1]s/.+", rName))), resource.TestCheckResourceAttrPair(resourceName, "cluster_name", eksClusterResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "capacity_type", eks.CapacityTypesOnDemand), resource.TestCheckResourceAttr(resourceName, "disk_size", "20"), resource.TestCheckResourceAttr(resourceName, "instance_types.#", "1"), resource.TestCheckResourceAttr(resourceName, "labels.%", "0"), @@ -179,6 +180,32 @@ func TestAccAWSEksNodeGroup_AmiType(t *testing.T) { }) } +func TestAccAWSEksNodeGroup_CapacityType_Spot(t *testing.T) { + var nodeGroup1 eks.Nodegroup + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_node_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksNodeGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksNodeGroupConfigCapacityType(rName, eks.CapacityTypesSpot), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksNodeGroupExists(resourceName, &nodeGroup1), + resource.TestCheckResourceAttr(resourceName, "capacity_type", eks.CapacityTypesSpot), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSEksNodeGroup_DiskSize(t *testing.T) { var nodeGroup1 eks.Nodegroup rName := acctest.RandomWithPrefix("tf-acc-test") @@ -239,9 +266,10 @@ func TestAccAWSEksNodeGroup_ForceUpdateVersion(t *testing.T) { }) } -func TestAccAWSEksNodeGroup_InstanceTypes(t *testing.T) { +func TestAccAWSEksNodeGroup_InstanceTypes_Multiple(t *testing.T) { var nodeGroup1 eks.Nodegroup rName := acctest.RandomWithPrefix("tf-acc-test") + ec2InstanceTypeOfferingsDataSourceName := "data.aws_ec2_instance_type_offerings.available" resourceName := "aws_eks_node_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -250,7 +278,33 @@ func TestAccAWSEksNodeGroup_InstanceTypes(t *testing.T) { CheckDestroy: testAccCheckAWSEksNodeGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEksNodeGroupConfigInstanceTypes1(rName, "t3.large"), + Config: testAccAWSEksNodeGroupConfigInstanceTypesMultiple(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksNodeGroupExists(resourceName, &nodeGroup1), + resource.TestCheckResourceAttrPair(resourceName, "instance_types.#", ec2InstanceTypeOfferingsDataSourceName, "instance_types.#"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksNodeGroup_InstanceTypes_Single(t *testing.T) { + var nodeGroup1 eks.Nodegroup + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_node_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksNodeGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksNodeGroupConfigInstanceTypesSingle(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSEksNodeGroupExists(resourceName, &nodeGroup1), resource.TestCheckResourceAttr(resourceName, "instance_types.#", "1"), @@ -1051,6 +1105,30 @@ resource "aws_eks_node_group" "test" { `, rName, amiType) } +func testAccAWSEksNodeGroupConfigCapacityType(rName, capacityType string) string { + return testAccAWSEksNodeGroupConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_node_group" "test" { + capacity_type = %[2]q + cluster_name = aws_eks_cluster.test.name + node_group_name = %[1]q + node_role_arn = aws_iam_role.node.arn + subnet_ids = aws_subnet.test[*].id + + scaling_config { + desired_size = 1 + max_size = 1 + min_size = 1 + } + + depends_on = [ + "aws_iam_role_policy_attachment.node-AmazonEKSWorkerNodePolicy", + "aws_iam_role_policy_attachment.node-AmazonEKS_CNI_Policy", + "aws_iam_role_policy_attachment.node-AmazonEC2ContainerRegistryReadOnly", + ] +} +`, rName, capacityType) +} + func testAccAWSEksNodeGroupConfigDiskSize(rName string, diskSize int) string { return testAccAWSEksNodeGroupConfigBase(rName) + fmt.Sprintf(` resource "aws_eks_node_group" "test" { @@ -1100,11 +1178,55 @@ resource "aws_eks_node_group" "test" { `, rName) } -func testAccAWSEksNodeGroupConfigInstanceTypes1(rName, instanceType1 string) string { - return testAccAWSEksNodeGroupConfigBase(rName) + fmt.Sprintf(` +func testAccAWSEksNodeGroupConfigInstanceTypesMultiple(rName string) string { + return composeConfig( + testAccAWSEksNodeGroupConfigBase(rName), + fmt.Sprintf(` +data "aws_ec2_instance_type_offerings" "available" { + filter { + name = "instance-type" + values = ["t3.medium", "t3.large", "t2.medium", "t2.large"] + } +} + +resource "aws_eks_node_group" "test" { + cluster_name = aws_eks_cluster.test.name + instance_types = data.aws_ec2_instance_type_offerings.available.instance_types + node_group_name = %[1]q + node_role_arn = aws_iam_role.node.arn + subnet_ids = aws_subnet.test[*].id + + scaling_config { + desired_size = 1 + max_size = 1 + min_size = 1 + } + + depends_on = [ + "aws_iam_role_policy_attachment.node-AmazonEKSWorkerNodePolicy", + "aws_iam_role_policy_attachment.node-AmazonEKS_CNI_Policy", + "aws_iam_role_policy_attachment.node-AmazonEC2ContainerRegistryReadOnly", + ] +} +`, rName)) +} + +func testAccAWSEksNodeGroupConfigInstanceTypesSingle(rName string) string { + return composeConfig( + testAccAWSEksNodeGroupConfigBase(rName), + fmt.Sprintf(` +data "aws_ec2_instance_type_offering" "available" { + filter { + name = "instance-type" + values = ["t3.large", "t2.large"] + } + + preferred_instance_types = ["t3.large", "t2.large"] +} + resource "aws_eks_node_group" "test" { cluster_name = aws_eks_cluster.test.name - instance_types = [%[2]q] + instance_types = [data.aws_ec2_instance_type_offering.available.instance_type] node_group_name = %[1]q node_role_arn = aws_iam_role.node.arn subnet_ids = aws_subnet.test[*].id @@ -1121,7 +1243,7 @@ resource "aws_eks_node_group" "test" { "aws_iam_role_policy_attachment.node-AmazonEC2ContainerRegistryReadOnly", ] } -`, rName, instanceType1) +`, rName)) } func testAccAWSEksNodeGroupConfigLabels1(rName, labelKey1, labelValue1 string) string { diff --git a/website/docs/r/eks_node_group.html.markdown b/website/docs/r/eks_node_group.html.markdown index de8ac264b8b..56bb8c52c1e 100644 --- a/website/docs/r/eks_node_group.html.markdown +++ b/website/docs/r/eks_node_group.html.markdown @@ -124,9 +124,10 @@ The following arguments are required: The following arguments are optional: * `ami_type` - (Optional) Type of Amazon Machine Image (AMI) associated with the EKS Node Group. Defaults to `AL2_x86_64`. Valid values: `AL2_x86_64`, `AL2_x86_64_GPU`, `AL2_ARM_64`. Terraform will only perform drift detection if a configuration value is provided. +* `capacity_type` - (Optional) Type of capacity associated with the EKS Node Group. Defaults to `ON_DEMAND`. Valid values: `ON_DEMAND`, `SPOT`. * `disk_size` - (Optional) Disk size in GiB for worker nodes. Defaults to `20`. Terraform will only perform drift detection if a configuration value is provided. * `force_update_version` - (Optional) Force version update if existing pods are unable to be drained due to a pod disruption budget issue. -* `instance_types` - (Optional) Set of instance types associated with the EKS Node Group. Defaults to `["t3.medium"]`. Terraform will only perform drift detection if a configuration value is provided. Currently, the EKS API only accepts a single value in the set. +* `instance_types` - (Optional) List of instance types associated with the EKS Node Group. Defaults to `["t3.medium"]`. Terraform will only perform drift detection if a configuration value is provided. * `labels` - (Optional) Key-value map of Kubernetes labels. Only labels that are applied with the EKS API are managed by this argument. Other Kubernetes labels applied to the EKS Node Group will not be managed. * `launch_template` - (Optional) Configuration block with Launch Template settings. Detailed below. * `release_version` – (Optional) AMI version of the EKS Node Group. Defaults to latest version for Kubernetes version.