Skip to content

Commit

Permalink
lambda_function: add layers support to resource and data source
Browse files Browse the repository at this point in the history
refs #6651
- add layers attribute to resource and data source
- update documentation
  • Loading branch information
acburdine committed Jan 15, 2019
1 parent 1069ec3 commit afa1b5f
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 0 deletions.
8 changes: 8 additions & 0 deletions aws/data_source_aws_lambda_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ func dataSourceAwsLambdaFunction() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"layers": {
Type: schema.TypeList,
Computed: true,
MaxItems: 5,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"memory_size": {
Type: schema.TypeInt,
Computed: true,
Expand Down
47 changes: 47 additions & 0 deletions aws/data_source_aws_lambda_function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,29 @@ func TestAccDataSourceAWSLambdaFunction_alias(t *testing.T) {
})
}

func TestAccDataSourceAWSLambdaFunction_layers(t *testing.T) {
rString := acctest.RandString(7)
roleName := fmt.Sprintf("tf-acctest-d-lambda-function-layer-role-%s", rString)
policyName := fmt.Sprintf("tf-acctest-d-lambda-function-layer-policy-%s", rString)
sgName := fmt.Sprintf("tf-acctest-d-lambda-function-layer-sg-%s", rString)
funcName := fmt.Sprintf("tf-acctest-d-lambda-function-layer-func-%s", rString)
layerName := fmt.Sprintf("tf-acctest-d-lambda-function-layer-layer-%s", rString)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAWSLambdaFunctionConfigLayers(roleName, policyName, sgName, funcName, layerName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.aws_lambda_function.acctest", "arn"),
resource.TestCheckResourceAttr("data.aws_lambda_function.acctest", "layers.#", "1"),
),
},
},
})
}

func TestAccDataSourceAWSLambdaFunction_vpc(t *testing.T) {
rString := acctest.RandString(7)
roleName := fmt.Sprintf("tf-acctest-d-lambda-function-vpc-role-%s", rString)
Expand Down Expand Up @@ -293,6 +316,30 @@ data "aws_lambda_function" "acctest" {
`, funcName, funcName)
}

func testAccDataSourceAWSLambdaFunctionConfigLayers(roleName, policyName, sgName, funcName, layerName string) string {
return fmt.Sprintf(testAccDataSourceAWSLambdaFunctionConfigBase(roleName, policyName, sgName)+`
resource "aws_lambda_layer_version" "acctest_create" {
filename = "test-fixtures/lambdatest.zip"
layer_name = "%s"
compatible_runtimes = ["nodejs8.10"]
}
resource "aws_lambda_function" "acctest_create" {
function_name = "%s"
description = "%s"
filename = "test-fixtures/lambdatest.zip"
role = "${aws_iam_role.lambda.arn}"
handler = "exports.example"
runtime = "nodejs8.10"
layers = ["${aws_lambda_layer_version.acctest_create.layer_arn}"]
}
data "aws_lambda_function" "acctest" {
function_name = "${aws_lambda_function.acctest_create.function_name}"
}
`, layerName, funcName, funcName)
}

func testAccDataSourceAWSLambdaFunctionConfigVPC(roleName, policyName, sgName, funcName string) string {
return fmt.Sprintf(testAccDataSourceAWSLambdaFunctionConfigBase(roleName, policyName, sgName)+`
resource "aws_lambda_function" "acctest_create" {
Expand Down
24 changes: 24 additions & 0 deletions aws/resource_aws_lambda_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ func resourceAwsLambdaFunction() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
"layers": {
Type: schema.TypeList,
Optional: true,
MaxItems: 5,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateArn,
},
},
"memory_size": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -317,6 +326,10 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
Publish: aws.Bool(d.Get("publish").(bool)),
}

if v, ok := d.GetOk("layers"); ok && len(v.([]interface{})) > 0 {
params.Layers = expandStringList(v.([]interface{}))
}

if v, ok := d.GetOk("dead_letter_config"); ok {
dlcMaps := v.([]interface{})
if len(dlcMaps) == 1 { // Schema guarantees either 0 or 1
Expand Down Expand Up @@ -510,6 +523,12 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err
d.Set("source_code_hash", function.CodeSha256)
d.Set("source_code_size", function.CodeSize)

layers := flattenLambdaLayers(function.Layers)
log.Printf("[INFO] Setting Lambda %s Layers %#v from API", d.Id(), layers)
if err := d.Set("layers", layers); err != nil {
return fmt.Errorf("Error setting layers for Lambda Function (%s): %s", d.Id(), err)
}

config := flattenLambdaVpcConfigResponse(function.VpcConfig)
log.Printf("[INFO] Setting Lambda %s VPC config %#v from API", d.Id(), config)
if err := d.Set("vpc_config", config); err != nil {
Expand Down Expand Up @@ -664,6 +683,11 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e
configReq.KMSKeyArn = aws.String(d.Get("kms_key_arn").(string))
configUpdate = true
}
if d.HasChange("layers") {
layers := d.Get("layers").([]interface{})
configReq.Layers = expandStringList(layers)
configUpdate = true
}
if d.HasChange("dead_letter_config") {
dlcMaps := d.Get("dead_letter_config").([]interface{})
configReq.DeadLetterConfig = &lambda.DeadLetterConfig{
Expand Down
116 changes: 116 additions & 0 deletions aws/resource_aws_lambda_function_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,75 @@ func TestAccAWSLambdaFunction_tracingConfig(t *testing.T) {
})
}

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

rString := acctest.RandString(8)
funcName := fmt.Sprintf("tf_acc_lambda_func_layer_%s", rString)
layerName := fmt.Sprintf("tf_acc_layer_lambda_func_layer_%s", rString)
policyName := fmt.Sprintf("tf_acc_policy_lambda_func_layer_%s", rString)
roleName := fmt.Sprintf("tf_acc_role_lambda_func_layer_%s", rString)
sgName := fmt.Sprintf("tf_acc_sg_lambda_func_layer_%s", rString)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLambdaConfigWithLayers(funcName, layerName, policyName, roleName, sgName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", funcName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, funcName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+funcName),
testAccCheckAWSLambdaFunctionVersion(&conf, "$LATEST"),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "layers.#", "1"),
),
},
},
})
}

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

rString := acctest.RandString(8)
funcName := fmt.Sprintf("tf_acc_lambda_func_layer_%s", rString)
layerName := fmt.Sprintf("tf_acc_lambda_layer_%s", rString)
layer2Name := fmt.Sprintf("tf_acc_lambda_layer2_%s", rString)
policyName := fmt.Sprintf("tf_acc_policy_lambda_func_vpc_%s", rString)
roleName := fmt.Sprintf("tf_acc_role_lambda_func_vpc_%s", rString)
sgName := fmt.Sprintf("tf_acc_sg_lambda_func_vpc_%s", rString)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLambdaConfigWithLayers(funcName, layerName, policyName, roleName, sgName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", funcName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, funcName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+funcName),
testAccCheckAWSLambdaFunctionVersion(&conf, "$LATEST"),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "layers.#", "1"),
),
},
{
Config: testAccAWSLambdaConfigWithLayersUpdated(funcName, layerName, layer2Name, policyName, roleName, sgName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", funcName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, funcName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+funcName),
testAccCheckAWSLambdaFunctionVersion(&conf, "$LATEST"),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "layers.#", "2"),
),
},
},
})
}

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

Expand Down Expand Up @@ -1888,6 +1957,53 @@ resource "aws_lambda_function" "lambda_function_test" {
`, funcName)
}

func testAccAWSLambdaConfigWithLayers(funcName, layerName, policyName, roleName, sgName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig(policyName, roleName, sgName)+`
resource "aws_lambda_layer_version" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
layer_name = "%s"
compatible_runtimes = ["nodejs8.10"]
}
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
runtime = "nodejs8.10"
layers = ["${aws_lambda_layer_version.lambda_function_test.layer_arn}"]
}
`, layerName, funcName)
}

func testAccAWSLambdaConfigWithLayersUpdated(funcName, layerName, layer2Name, policyName, roleName, sgName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig(policyName, roleName, sgName)+`
resource "aws_lambda_layer_version" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
layer_name = "%s"
compatible_runtimes = ["nodejs8.10"]
}
resource "aws_lambda_layer_version" "lambda_function_test_2" {
filename = "test-fixtures/lambdatest_modified.zip"
layer_name = "%s"
compatible_runtimes = ["nodejs8.10"]
}
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
runtime = "nodejs8.10"
layers = [
"${aws_lambda_layer_version.lambda_function_test.layer_arn}",
"${aws_lambda_layer_version.lambda_function_test_2.layer_arn}",
]
}
`, layerName, layer2Name, funcName)
}

func testAccAWSLambdaConfigWithVPC(funcName, policyName, roleName, sgName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig(policyName, roleName, sgName)+`
resource "aws_lambda_function" "lambda_function_test" {
Expand Down
8 changes: 8 additions & 0 deletions aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,14 @@ func flattenLambdaEnvironment(lambdaEnv *lambda.EnvironmentResponse) []interface
return []interface{}{envs}
}

func flattenLambdaLayers(layers []*lambda.Layer) []interface{} {
arns := make([]*string, len(layers))
for i, layer := range layers {
arns[i] = layer.Arn
}
return flattenStringList(arns)
}

func flattenLambdaVpcConfigResponse(s *lambda.VpcConfigResponse) []map[string]interface{} {
settings := make(map[string]interface{})

Expand Down
1 change: 1 addition & 0 deletions website/docs/d/lambda_function.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ In addition to all arguments above, the following attributes are exported:
* `invoke_arn` - The ARN to be used for invoking Lambda Function from API Gateway.
* `kms_key_arn` - The ARN for the KMS encryption key.
* `last_modified` - The date this resource was last modified.
* `layers` - A list of Lambda Layer ARNs attached to your Lambda Function.
* `memory_size` - Amount of memory in MB your Lambda Function can use at runtime.
* `qualified_arn` - The Amazon Resource Name (ARN) identifying your Lambda Function Version
* `reserved_concurrent_executions` - The amount of reserved concurrent executions for this lambda function.
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/lambda_function.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ large files efficiently.
* `handler` - (Required) The function [entrypoint][3] in your code.
* `role` - (Required) IAM role attached to the Lambda Function. This governs both who / what can invoke your Lambda Function, as well as what resources our Lambda Function has access to. See [Lambda Permission Model][4] for more details.
* `description` - (Optional) Description of what your Lambda Function does.
* `layers` - (Optional) List of Lambda Layer Version ARNs (maximum of 5) to attach to your Lambda Function. See [Lambda Layers][10]
* `memory_size` - (Optional) Amount of memory in MB your Lambda Function can use at runtime. Defaults to `128`. See [Limits][5]
* `runtime` - (Required) See [Runtimes][6] for valid values.
* `timeout` - (Optional) The amount of time your Lambda Function has to run in seconds. Defaults to `3`. See [Limits][5]
Expand Down Expand Up @@ -174,6 +175,7 @@ For **environment** the following attributes are supported:
[7]: http://docs.aws.amazon.com/lambda/latest/dg/vpc.html
[8]: https://docs.aws.amazon.com/lambda/latest/dg/deployment-package-v2.html
[9]: https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html
[10]: https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html

## Timeouts

Expand Down

0 comments on commit afa1b5f

Please sign in to comment.