From eb2aaef016c7b72cc22b238cb98480495c19bddd Mon Sep 17 00:00:00 2001 From: Maxime Bury Date: Wed, 17 Feb 2016 20:06:18 -0500 Subject: [PATCH 1/4] VpcId can be nil in the wild --- builtin/providers/aws/structure.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index dd22d83c9..d257128b7 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -690,7 +690,9 @@ func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]in settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) - settings["vpc_id"] = *s.VpcId + if s.VpcId != nil { + settings["vpc_id"] = *s.VpcId + } return []map[string]interface{}{settings} } From f0f3c430a80c88ece8a3e43908d37c942ce3d6b3 Mon Sep 17 00:00:00 2001 From: Maxime Bury Date: Wed, 17 Feb 2016 21:45:13 -0500 Subject: [PATCH 2/4] Generalize fix to ignore empty VPC config --- builtin/providers/aws/structure.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index d257128b7..f033cb4a1 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -688,11 +688,14 @@ func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]in return nil } + // Empty + if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && s.VpcId == nil { + return nil + } + settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) - if s.VpcId != nil { - settings["vpc_id"] = *s.VpcId - } + settings["vpc_id"] = *s.VpcId return []map[string]interface{}{settings} } From 288ba868e4c8e7f56b40f0c3f27cc86894badada Mon Sep 17 00:00:00 2001 From: Maxime Bury Date: Thu, 18 Feb 2016 13:45:32 -0800 Subject: [PATCH 3/4] Harden things around VPC config --- .../aws/resource_aws_lambda_function.go | 64 ++++++++++++------- .../aws/resource_aws_lambda_function_test.go | 35 +++++++++- builtin/providers/aws/structure.go | 9 +-- 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/builtin/providers/aws/resource_aws_lambda_function.go b/builtin/providers/aws/resource_aws_lambda_function.go index 363a18af2..8d548e77e 100644 --- a/builtin/providers/aws/resource_aws_lambda_function.go +++ b/builtin/providers/aws/resource_aws_lambda_function.go @@ -173,32 +173,29 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e } if v, ok := d.GetOk("vpc_config"); ok { - configs := v.([]interface{}) + config, err := validateVPCConfig(v) + if err != nil { + return err + } - if len(configs) > 1 { - return errors.New("Only a single vpc_config block is expected") - } else if len(configs) == 1 { - config := configs[0].(map[string]interface{}) - var subnetIds []*string - for _, id := range config["subnet_ids"].(*schema.Set).List() { - subnetIds = append(subnetIds, aws.String(id.(string))) - } + var subnetIds []*string + for _, id := range config["subnet_ids"].(*schema.Set).List() { + subnetIds = append(subnetIds, aws.String(id.(string))) + } - var securityGroupIds []*string - for _, id := range config["security_group_ids"].(*schema.Set).List() { - securityGroupIds = append(securityGroupIds, aws.String(id.(string))) - } + var securityGroupIds []*string + for _, id := range config["security_group_ids"].(*schema.Set).List() { + securityGroupIds = append(securityGroupIds, aws.String(id.(string))) + } - var vpcConfig = &lambda.VpcConfig{ - SubnetIds: subnetIds, - SecurityGroupIds: securityGroupIds, - } - params.VpcConfig = vpcConfig + params.VpcConfig = &lambda.VpcConfig{ + SubnetIds: subnetIds, + SecurityGroupIds: securityGroupIds, } } // IAM profiles can take ~10 seconds to propagate in AWS: - // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console + // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console // Error creating Lambda function: InvalidParameterValueException: The role defined for the task cannot be assumed by Lambda. err := resource.Retry(1*time.Minute, func() error { _, err := conn.CreateFunction(params) @@ -257,7 +254,9 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err d.Set("role", function.Role) d.Set("runtime", function.Runtime) d.Set("timeout", function.Timeout) - d.Set("vpc_config", flattenLambdaVpcConfigResponse(function.VpcConfig)) + if config := flattenLambdaVpcConfigResponse(function.VpcConfig); len(config) > 0 { + d.Set("vpc_config", config) + } return nil } @@ -286,7 +285,28 @@ func resourceAwsLambdaFunctionDelete(d *schema.ResourceData, meta interface{}) e // resourceAwsLambdaFunctionUpdate maps to: // UpdateFunctionCode in the API / SDK func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) error { - // conn := meta.(*AWSClient).lambdaconn - return nil } + +func validateVPCConfig(v interface{}) (map[string]interface{}, error) { + configs := v.([]interface{}) + if len(configs) > 1 { + return nil, errors.New("Only a single vpc_config block is expected") + } + + config, ok := configs[0].(map[string]interface{}) + + if !ok { + return nil, errors.New("vpc_config is ") + } + + if config["subnet_ids"].(*schema.Set).Len() == 0 { + return nil, errors.New("vpc_config.subnet_ids cannot be empty") + } + + if config["security_group_ids"].(*schema.Set).Len() == 0 { + return nil, errors.New("vpc_config.security_group_ids cannot be empty") + } + + return config, nil +} diff --git a/builtin/providers/aws/resource_aws_lambda_function_test.go b/builtin/providers/aws/resource_aws_lambda_function_test.go index ff6f92bd4..8b4d3fdf5 100644 --- a/builtin/providers/aws/resource_aws_lambda_function_test.go +++ b/builtin/providers/aws/resource_aws_lambda_function_test.go @@ -19,7 +19,26 @@ func TestAccAWSLambdaFunction_basic(t *testing.T) { CheckDestroy: testAccCheckLambdaFunctionDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSLambdaConfig, + Config: testAccAWSLambdaConfigBasic, + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", &conf), + testAccCheckAWSLambdaAttributes(&conf), + ), + }, + }, + }) +} + +func TestAccAWSLambdaFunction_VPC(t *testing.T) { + var conf lambda.GetFunctionOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLambdaFunctionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLambdaConfigWithVPC, Check: resource.ComposeTestCheckFunc( testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", &conf), testAccCheckAWSLambdaAttributes(&conf), @@ -96,7 +115,7 @@ func testAccCheckAWSLambdaAttributes(function *lambda.GetFunctionOutput) resourc } } -const testAccAWSLambdaConfig = ` +const baseAccAWSLambdaConfig = ` resource "aws_iam_role_policy" "iam_policy_for_lambda" { name = "iam_policy_for_lambda" role = "${aws_iam_role.iam_for_lambda.id}" @@ -179,6 +198,18 @@ resource "aws_security_group" "sg_for_lambda" { } } +` + +const testAccAWSLambdaConfigBasic = baseAccAWSLambdaConfig + ` +resource "aws_lambda_function" "lambda_function_test" { + filename = "test-fixtures/lambdatest.zip" + function_name = "example_lambda_name" + role = "${aws_iam_role.iam_for_lambda.arn}" + handler = "exports.example" +} +` + +const testAccAWSLambdaConfigWithVPC = baseAccAWSLambdaConfig + ` resource "aws_lambda_function" "lambda_function_test" { filename = "test-fixtures/lambdatest.zip" function_name = "example_lambda_name" diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index f033cb4a1..d257128b7 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -688,14 +688,11 @@ func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]in return nil } - // Empty - if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && s.VpcId == nil { - return nil - } - settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) - settings["vpc_id"] = *s.VpcId + if s.VpcId != nil { + settings["vpc_id"] = *s.VpcId + } return []map[string]interface{}{settings} } From f92b2b7c5802fe3d17696416db12114df45a1c15 Mon Sep 17 00:00:00 2001 From: Maxime Bury Date: Tue, 23 Feb 2016 12:56:46 -0800 Subject: [PATCH 4/4] Also ignore empty VPC config after all --- builtin/providers/aws/structure.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index d257128b7..6facdf792 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -688,6 +688,10 @@ func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]in return nil } + if len(s.SubnetIds) == 0 && len(s.SecurityGroupIds) == 0 && s.VpcId == nil { + return nil + } + settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) settings["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SecurityGroupIds)) if s.VpcId != nil {