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

Only set associate_public_ip_address if it's explicitly set #10157

Merged
merged 5 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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