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

service/ec2: Updated handling for Lambda Hyperplane ENIs #10347

Merged
merged 5 commits into from
Oct 3, 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
104 changes: 104 additions & 0 deletions aws/resource_aws_lambda_function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,33 @@ func TestAccAWSLambdaFunction_VPC_withInvocation(t *testing.T) {
})
}

// Reference: https://github.com/terraform-providers/terraform-provider-aws/issues/10044
func TestAccAWSLambdaFunction_VpcConfig_ProperIamDependencies(t *testing.T) {
var function lambda.GetFunctionOutput

rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_lambda_function.test"
vpcResourceName := "aws_vpc.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLambdaConfigVpcConfigProperIamDependencies(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists(resourceName, rName, &function),
resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "vpc_config.0.subnet_ids.#", "1"),
resource.TestCheckResourceAttr(resourceName, "vpc_config.0.security_group_ids.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "vpc_config.0.vpc_id", vpcResourceName, "id"),
),
},
},
})
}

func TestAccAWSLambdaFunction_EmptyVpcConfig(t *testing.T) {
var conf lambda.GetFunctionOutput

Expand Down Expand Up @@ -1873,6 +1900,83 @@ resource "aws_lambda_function" "lambda_function_test" {
`, fileName, funcName)
}

func testAccAWSLambdaConfigVpcConfigProperIamDependencies(rName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}

resource "aws_iam_role_policy_attachment" "test" {
policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
role = "${aws_iam_role.test.id}"
}

resource "aws_iam_role" "test" {
name = %[1]q

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = %[1]q
}
}

resource "aws_subnet" "test" {
depends_on = ["aws_iam_role_policy_attachment.test"]

vpc_id = "${aws_vpc.test.id}"
cidr_block = "10.0.0.0/24"

tags = {
Name = %[1]q
}
}

resource "aws_security_group" "test" {
depends_on = ["aws_iam_role_policy_attachment.test"]

name = %[1]q
vpc_id = "${aws_vpc.test.id}"

egress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 0
protocol = "-1"
to_port = 0
}
}

resource "aws_lambda_function" "test" {
filename = "test-fixtures/lambdatest.zip"
function_name = %[1]q
role = "${aws_iam_role.test.arn}"
handler = "exports.example"
runtime = "nodejs8.10"

vpc_config {
subnet_ids = ["${aws_subnet.test.id}"]
security_group_ids = ["${aws_security_group.test.id}"]
}
}
`, rName)
}

func testAccAWSLambdaConfigWithTracingConfig(funcName, policyName, roleName, sgName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig(policyName, roleName, sgName)+`
resource "aws_lambda_function" "lambda_function_test" {
Expand Down
131 changes: 131 additions & 0 deletions aws/resource_aws_network_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,134 @@ func resourceAwsEniAttachmentHash(v interface{}) int {
buf.WriteString(fmt.Sprintf("%d-", m["device_index"].(int)))
return hashcode.String(buf.String())
}

func deleteNetworkInterface(conn *ec2.EC2, eniId string) error {
_, err := conn.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{
NetworkInterfaceId: aws.String(eniId),
})

if isAWSErr(err, "InvalidNetworkInterfaceID.NotFound", "") {
return nil
}

if err != nil {
return fmt.Errorf("error deleting ENI (%s): %s", eniId, err)
}

return nil
}

func detachNetworkInterface(conn *ec2.EC2, eni *ec2.NetworkInterface, timeout time.Duration) error {
if eni == nil {
return nil
}

eniId := aws.StringValue(eni.NetworkInterfaceId)
if eni.Attachment == nil {
log.Printf("[DEBUG] ENI %s is already detached", eniId)
return nil
}

_, err := conn.DetachNetworkInterface(&ec2.DetachNetworkInterfaceInput{
AttachmentId: eni.Attachment.AttachmentId,
Force: aws.Bool(true),
})

if isAWSErr(err, "InvalidAttachmentID.NotFound", "") {
return nil
}

if err != nil {
return fmt.Errorf("error detaching ENI (%s): %s", eniId, err)
}

stateConf := &resource.StateChangeConf{
Pending: []string{
ec2.AttachmentStatusAttaching,
ec2.AttachmentStatusAttached,
ec2.AttachmentStatusDetaching,
},
Target: []string{
ec2.AttachmentStatusDetached,
},
Refresh: networkInterfaceAttachmentStateRefresh(conn, eniId),
Timeout: timeout,
Delay: 10 * time.Second,
MinTimeout: 5 * time.Second,
NotFoundChecks: 1,
}

log.Printf("[DEBUG] Waiting for ENI (%s) to become detached", eniId)
_, err = stateConf.WaitForState()

if isResourceNotFoundError(err) {
return nil
}

if err != nil {
return fmt.Errorf("error waiting for ENI (%s) to become detached: %s", eniId, err)
}

return nil
}

func networkInterfaceAttachmentStateRefresh(conn *ec2.EC2, eniId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
NetworkInterfaceIds: aws.StringSlice([]string{eniId}),
})

if isAWSErr(err, "InvalidNetworkInterfaceID.NotFound", "") {
return nil, ec2.AttachmentStatusDetached, nil
}

if err != nil {
return nil, "", fmt.Errorf("error describing ENI (%s): %s", eniId, err)
}

n := len(resp.NetworkInterfaces)
switch n {
case 0:
return nil, ec2.AttachmentStatusDetached, nil

case 1:
attachment := resp.NetworkInterfaces[0].Attachment
if attachment == nil {
return nil, ec2.AttachmentStatusDetached, nil
}
return attachment, aws.StringValue(attachment.Status), nil

default:
return nil, "", fmt.Errorf("found %d ENIs for %s, expected 1", n, eniId)
}
}
}

func networkInterfaceStateRefresh(conn *ec2.EC2, eniId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.DescribeNetworkInterfaces(&ec2.DescribeNetworkInterfacesInput{
NetworkInterfaceIds: aws.StringSlice([]string{eniId}),
})

if isAWSErr(err, "InvalidNetworkInterfaceID.NotFound", "") {
return nil, "", nil
}

if err != nil {
return nil, "", fmt.Errorf("error describing ENI (%s): %s", eniId, err)
}

n := len(resp.NetworkInterfaces)
switch n {
case 0:
return nil, "", nil

case 1:
eni := resp.NetworkInterfaces[0]
return eni, aws.StringValue(eni.Status), nil

default:
return nil, "", fmt.Errorf("found %d ENIs for %s, expected 1", n, eniId)
}
}
}
Loading