From 03df8360cb98c83d4486d450e77412d7c448f46c Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 4 Sep 2016 09:55:13 +0100 Subject: [PATCH] provider/aws: Support 'publish' attribute in lambda_function --- .../aws/import_aws_lambda_function_test.go | 6 +-- .../aws/resource_aws_lambda_function.go | 54 +++++++++++++++++++ .../aws/resource_aws_lambda_function_test.go | 38 +++++++++++++ .../aws/r/lambda_function.html.markdown | 4 ++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/import_aws_lambda_function_test.go b/builtin/providers/aws/import_aws_lambda_function_test.go index 3672c2e109..2ab7eac6b9 100644 --- a/builtin/providers/aws/import_aws_lambda_function_test.go +++ b/builtin/providers/aws/import_aws_lambda_function_test.go @@ -26,7 +26,7 @@ func TestAccAWSLambdaFunction_importLocalFile(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"filename"}, + ImportStateVerifyIgnore: []string{"filename", "publish"}, }, }, }) @@ -50,7 +50,7 @@ func TestAccAWSLambdaFunction_importLocalFile_VPC(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"filename"}, + ImportStateVerifyIgnore: []string{"filename", "publish"}, }, }, }) @@ -74,7 +74,7 @@ func TestAccAWSLambdaFunction_importS3(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"s3_bucket", "s3_key"}, + ImportStateVerifyIgnore: []string{"s3_bucket", "s3_key", "publish"}, }, }, }) diff --git a/builtin/providers/aws/resource_aws_lambda_function.go b/builtin/providers/aws/resource_aws_lambda_function.go index 84e667ecc4..4fcc102b3a 100644 --- a/builtin/providers/aws/resource_aws_lambda_function.go +++ b/builtin/providers/aws/resource_aws_lambda_function.go @@ -85,6 +85,15 @@ func resourceAwsLambdaFunction() *schema.Resource { Optional: true, Default: 3, }, + "publish": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, "vpc_config": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -116,6 +125,10 @@ func resourceAwsLambdaFunction() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "qualified_arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, "last_modified": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -173,6 +186,7 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e Role: aws.String(iamRole), Runtime: aws.String(d.Get("runtime").(string)), Timeout: aws.Int64(int64(d.Get("timeout").(int))), + Publish: aws.Bool(d.Get("publish").(bool)), } if v, ok := d.GetOk("vpc_config"); ok { @@ -270,6 +284,45 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err } d.Set("source_code_hash", function.CodeSha256) + // List is sorted from oldest to latest + // so this may get costly over time :'( + var lastVersion, lastQualifiedArn string + err = listVersionsByFunctionPages(conn, &lambda.ListVersionsByFunctionInput{ + FunctionName: function.FunctionName, + MaxItems: aws.Int64(10000), + }, func(p *lambda.ListVersionsByFunctionOutput, lastPage bool) bool { + if lastPage { + last := p.Versions[len(p.Versions)-1] + lastVersion = *last.Version + lastQualifiedArn = *last.FunctionArn + return true + } + return false + }) + if err != nil { + return err + } + + d.Set("version", lastVersion) + d.Set("qualified_arn", lastQualifiedArn) + + return nil +} + +func listVersionsByFunctionPages(c *lambda.Lambda, input *lambda.ListVersionsByFunctionInput, + fn func(p *lambda.ListVersionsByFunctionOutput, lastPage bool) bool) error { + for { + page, err := c.ListVersionsByFunction(input) + if err != nil { + return err + } + lastPage := page.NextMarker == nil + + shouldContinue := fn(page, lastPage) + if !shouldContinue || lastPage { + break + } + } return nil } @@ -304,6 +357,7 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e if d.HasChange("filename") || d.HasChange("source_code_hash") || d.HasChange("s3_bucket") || d.HasChange("s3_key") || d.HasChange("s3_object_version") { codeReq := &lambda.UpdateFunctionCodeInput{ FunctionName: aws.String(d.Id()), + Publish: aws.Bool(d.Get("publish").(bool)), } if v, ok := d.GetOk("filename"); ok { diff --git a/builtin/providers/aws/resource_aws_lambda_function_test.go b/builtin/providers/aws/resource_aws_lambda_function_test.go index 2ae453c207..2d3e6c0e89 100644 --- a/builtin/providers/aws/resource_aws_lambda_function_test.go +++ b/builtin/providers/aws/resource_aws_lambda_function_test.go @@ -39,6 +39,32 @@ func TestAccAWSLambdaFunction_basic(t *testing.T) { }) } +func TestAccAWSLambdaFunction_versioned(t *testing.T) { + var conf lambda.GetFunctionOutput + + rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLambdaFunctionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSLambdaConfigVersioned(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf), + testAccCheckAwsLambdaFunctionName(&conf, rName), + testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName), + resource.TestMatchResourceAttr("aws_lambda_function.lambda_function_test", "version", + regexp.MustCompile("^[0-9]+$")), + resource.TestMatchResourceAttr("aws_lambda_function.lambda_function_test", "qualified_arn", + regexp.MustCompile(":"+rName+":[0-9]+$")), + ), + }, + }, + }) +} + func TestAccAWSLambdaFunction_VPC(t *testing.T) { var conf lambda.GetFunctionOutput rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5)) @@ -518,6 +544,18 @@ resource "aws_lambda_function" "lambda_function_test" { `, rName) } +func testAccAWSLambdaConfigVersioned(rName string) string { + return fmt.Sprintf(baseAccAWSLambdaConfig+` +resource "aws_lambda_function" "lambda_function_test" { + filename = "test-fixtures/lambdatest.zip" + function_name = "%s" + publish = true + role = "${aws_iam_role.iam_for_lambda.arn}" + handler = "exports.example" +} +`, rName) +} + func testAccAWSLambdaConfigWithVPC(rName string) string { return fmt.Sprintf(baseAccAWSLambdaConfig+` resource "aws_lambda_function" "lambda_function_test" { diff --git a/website/source/docs/providers/aws/r/lambda_function.html.markdown b/website/source/docs/providers/aws/r/lambda_function.html.markdown index 16e3d35b03..5240fe107c 100644 --- a/website/source/docs/providers/aws/r/lambda_function.html.markdown +++ b/website/source/docs/providers/aws/r/lambda_function.html.markdown @@ -56,6 +56,7 @@ resource "aws_lambda_function" "test_lambda" { * `memory_size` - (Optional) Amount of memory in MB your Lambda Function can use at runtime. Defaults to `128`. See [Limits][5] * `runtime` - (Optional) Defaults to `nodejs`. 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] +* `publish` - (Optional) Whether to publish creation/change as new Lambda Function Version. Defaults to `false`. * `vpc_config` - (Optional) Provide this to allow your function to access your VPC. Fields documented below. See [Lambda in VPC][7] * `source_code_hash` - (Optional) Used to trigger updates. This is only useful in conjuction with `filename`. The only useful value is `${base64sha256(file("file.zip"))}`. @@ -70,6 +71,9 @@ resource "aws_lambda_function" "test_lambda" { ## Attributes Reference * `arn` - The Amazon Resource Name (ARN) identifying your Lambda Function. +* `qualified_arn` - The Amazon Resource Name (ARN) identifying your Lambda Function Version + (if versioning is enabled via `publish = true`). +* `version` - Latest published version of your Lambda Function * `last_modified` - The date this resource was last modified. * `source_code_hash` - Base64-encoded representation of raw SHA-256 sum of the zip file provided either via `filename` or `s3_*` parameters