Skip to content

Commit

Permalink
Merge pull request #10157 from terraform-providers/rfd-at6777
Browse files Browse the repository at this point in the history
Only set associate_public_ip_address if it's explicitly set
  • Loading branch information
ryndaniels authored Nov 27, 2019
2 parents f14be52 + f2c04a0 commit 59e99f2
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 17 deletions.
179 changes: 179 additions & 0 deletions aws/resource_aws_ec2_fleet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"errors"
"fmt"
"strconv"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
Expand Down Expand Up @@ -986,6 +988,183 @@ func TestAccAWSEc2Fleet_Type(t *testing.T) {
})
}

// Test for the bug described in https://github.com/terraform-providers/terraform-provider-aws/issues/6777
func TestAccAWSEc2Fleet_TemplateMultipleNetworkInterfaces(t *testing.T) {
var fleet1 ec2.FleetData
resourceName := "aws_ec2_fleet.test"
rInt := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2Fleet(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSEc2FleetDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2FleetConfig_multipleNetworkInterfaces(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSEc2FleetExists(resourceName, &fleet1),
resource.TestCheckResourceAttr(resourceName, "type", "maintain"),
testAccCheckAWSEc2FleetHistory(resourceName, "The associatePublicIPAddress parameter cannot be specified when launching with multiple network interfaces"),
),
},
},
})
}

func testAccAWSEc2FleetConfig_multipleNetworkInterfaces(rInt int) string {
return fmt.Sprintf(`
data "aws_ami" "test" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_internet_gateway" "test" {
vpc_id = "${aws_vpc.test.id}"
}
resource "aws_subnet" "test" {
cidr_block = "10.1.0.0/24"
vpc_id = "${aws_vpc.test.id}"
}
resource "aws_security_group" "test" {
name = "security-group-%d"
description = "Testacc SSH security group"
vpc_id = "${aws_vpc.test.id}"
ingress {
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
egress {
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_network_interface" "test" {
subnet_id = "${aws_subnet.test.id}"
security_groups = ["${aws_security_group.test.id}"]
}
resource "aws_launch_template" "test" {
name = "testacc-lt-%d"
image_id = "${data.aws_ami.test.id}"
instance_market_options {
spot_options {
spot_instance_type = "persistent"
}
market_type="spot"
}
network_interfaces {
device_index = 0
delete_on_termination = true
network_interface_id = "${aws_network_interface.test.id}"
}
network_interfaces {
device_index = 1
delete_on_termination = true
subnet_id = "${aws_subnet.test.id}"
}
}
resource "aws_ec2_fleet" "test" {
terminate_instances = true
launch_template_config {
launch_template_specification {
launch_template_id = "${aws_launch_template.test.id}"
version = "${aws_launch_template.test.latest_version}"
}
# allow to choose from several instance types if there is no spot capacity for some type
override {
instance_type = "t2.micro"
}
override {
instance_type = "t3.micro"
}
override {
instance_type = "t3.small"
}
}
target_capacity_specification {
default_target_capacity_type = "spot"
total_target_capacity = 1
}
}
`, rInt, rInt)
}

func testAccCheckAWSEc2FleetHistory(resourceName string, errorMsg string) resource.TestCheckFunc {
return func(s *terraform.State) error {
time.Sleep(time.Minute * 2) // We have to wait a bit for the history to get populated.

rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No EC2 Fleet ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).ec2conn

input := &ec2.DescribeFleetHistoryInput{
FleetId: aws.String(rs.Primary.ID),
StartTime: aws.Time(time.Now().Add(time.Hour * -2)),
}

output, err := conn.DescribeFleetHistory(input)

if err != nil {
return err
}

if output == nil {
return fmt.Errorf("EC2 Fleet history not found")
}

if output.HistoryRecords == nil {
return fmt.Errorf("No fleet history records found for fleet %s", rs.Primary.ID)
}

for _, record := range output.HistoryRecords {
if record == nil {
continue
}
if strings.Contains(aws.StringValue(record.EventInformation.EventDescription), errorMsg) {
return fmt.Errorf("Error %s found in fleet history event", errorMsg)
}
}

return nil
}
}

func testAccCheckAWSEc2FleetExists(resourceName string, fleet *ec2.FleetData) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down
45 changes: 29 additions & 16 deletions aws/resource_aws_launch_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,10 @@ func resourceAwsLaunchTemplate() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"associate_public_ip_address": {
Type: schema.TypeBool,
Optional: true,
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppressEquivalentTypeStringBoolean,
ValidateFunc: validateTypeStringNullableBoolean,
},
"delete_on_termination": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -921,15 +923,17 @@ func getNetworkInterfaces(n []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecifi
var ipv4Addresses []string

networkInterface := map[string]interface{}{
"associate_public_ip_address": aws.BoolValue(v.AssociatePublicIpAddress),
"delete_on_termination": aws.BoolValue(v.DeleteOnTermination),
"description": aws.StringValue(v.Description),
"device_index": aws.Int64Value(v.DeviceIndex),
"ipv4_address_count": aws.Int64Value(v.SecondaryPrivateIpAddressCount),
"ipv6_address_count": aws.Int64Value(v.Ipv6AddressCount),
"network_interface_id": aws.StringValue(v.NetworkInterfaceId),
"private_ip_address": aws.StringValue(v.PrivateIpAddress),
"subnet_id": aws.StringValue(v.SubnetId),
"delete_on_termination": aws.BoolValue(v.DeleteOnTermination),
"description": aws.StringValue(v.Description),
"device_index": aws.Int64Value(v.DeviceIndex),
"ipv4_address_count": aws.Int64Value(v.SecondaryPrivateIpAddressCount),
"ipv6_address_count": aws.Int64Value(v.Ipv6AddressCount),
"network_interface_id": aws.StringValue(v.NetworkInterfaceId),
"private_ip_address": aws.StringValue(v.PrivateIpAddress),
"subnet_id": aws.StringValue(v.SubnetId),
}
if v.AssociatePublicIpAddress != nil {
networkInterface["associate_public_ip_address"] = strconv.FormatBool(aws.BoolValue(v.AssociatePublicIpAddress))
}

if len(v.Ipv6Addresses) > 0 {
Expand Down Expand Up @@ -1149,7 +1153,10 @@ func buildLaunchTemplateData(d *schema.ResourceData) (*ec2.RequestLaunchTemplate
continue
}
niData := ni.(map[string]interface{})
networkInterface := readNetworkInterfacesFromConfig(niData)
networkInterface, err := readNetworkInterfacesFromConfig(niData)
if err != nil {
return nil, err
}
networkInterfaces = append(networkInterfaces, networkInterface)
}
opts.NetworkInterfaces = networkInterfaces
Expand Down Expand Up @@ -1256,7 +1263,7 @@ func readEbsBlockDeviceFromConfig(ebs map[string]interface{}) (*ec2.LaunchTempla
return ebsDevice, nil
}

func readNetworkInterfacesFromConfig(ni map[string]interface{}) *ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest {
func readNetworkInterfacesFromConfig(ni map[string]interface{}) (*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest, error) {
var ipv4Addresses []*ec2.PrivateIpAddressSpecification
var ipv6Addresses []*ec2.InstanceIpv6AddressRequest
var privateIpAddress string
Expand All @@ -1276,8 +1283,14 @@ func readNetworkInterfacesFromConfig(ni map[string]interface{}) *ec2.LaunchTempl

if v, ok := ni["network_interface_id"].(string); ok && v != "" {
networkInterface.NetworkInterfaceId = aws.String(v)
} else if v, ok := ni["associate_public_ip_address"]; ok {
networkInterface.AssociatePublicIpAddress = aws.Bool(v.(bool))
}

if v, ok := ni["associate_public_ip_address"]; ok && v.(string) != "" {
vBool, err := strconv.ParseBool(v.(string))
if err != nil {
return nil, fmt.Errorf("error converting associate_public_ip_address %q from string to boolean: %s", v.(string), err)
}
networkInterface.AssociatePublicIpAddress = aws.Bool(vBool)
}

if v, ok := ni["private_ip_address"].(string); ok && v != "" {
Expand Down Expand Up @@ -1320,7 +1333,7 @@ func readNetworkInterfacesFromConfig(ni map[string]interface{}) *ec2.LaunchTempl
networkInterface.PrivateIpAddresses = ipv4Addresses
}

return networkInterface
return networkInterface, nil
}

func readIamInstanceProfileFromConfig(iip map[string]interface{}) *ec2.LaunchTemplateIamInstanceProfileSpecificationRequest {
Expand Down
78 changes: 77 additions & 1 deletion aws/resource_aws_launch_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ func TestAccAWSLaunchTemplate_networkInterface(t *testing.T) {
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", "false"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv4_address_count", "2"),
),
},
Expand All @@ -554,6 +554,55 @@ func TestAccAWSLaunchTemplate_networkInterface(t *testing.T) {
})
}

func TestAccAWSLaunchTemplate_associatePublicIPAddress(t *testing.T) {
var template ec2.LaunchTemplate
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_launch_template.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLaunchTemplateDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLaunchTemplateConfig_associatePublicIpAddress(rName, "true"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", "true"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv4_address_count", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccAWSLaunchTemplateConfig_associatePublicIpAddress(rName, "false"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", "false"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv4_address_count", "2"),
),
},
{
Config: testAccAWSLaunchTemplateConfig_associatePublicIpAddress(rName, "null"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv4_address_count", "2"),
),
},
},
})
}

func TestAccAWSLaunchTemplate_networkInterface_ipv6Addresses(t *testing.T) {
var template ec2.LaunchTemplate
resourceName := "aws_launch_template.test"
Expand Down Expand Up @@ -1077,6 +1126,33 @@ resource "aws_launch_template" "test" {
}
`

func testAccAWSLaunchTemplateConfig_associatePublicIpAddress(rName, associatePublicIPAddress string) string {
return fmt.Sprintf(`
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "test" {
vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.1.0.0/24"
}
resource "aws_network_interface" "test" {
subnet_id = "${aws_subnet.test.id}"
}
resource "aws_launch_template" "test" {
name = %[1]q
network_interfaces {
network_interface_id = "${aws_network_interface.test.id}"
associate_public_ip_address = %[2]s
ipv4_address_count = 2
}
}
`, rName, associatePublicIPAddress)
}

const testAccAWSLaunchTemplateConfig_networkInterface_ipv6Addresses = `
resource "aws_launch_template" "test" {
name = "network-interface-ipv6-addresses-launch-template"
Expand Down

0 comments on commit 59e99f2

Please sign in to comment.