github.com/ottenhoff/terraform@v0.7.0-rc1.0.20160607213102-ac2d195cc560/builtin/providers/aws/resource_aws_lambda_function_test.go (about) 1 package aws 2 3 import ( 4 "archive/zip" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "regexp" 10 "strings" 11 "testing" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/service/lambda" 15 "github.com/hashicorp/terraform/helper/acctest" 16 "github.com/hashicorp/terraform/helper/resource" 17 "github.com/hashicorp/terraform/terraform" 18 ) 19 20 func TestAccAWSLambdaFunction_basic(t *testing.T) { 21 var conf lambda.GetFunctionOutput 22 23 rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5)) 24 25 resource.Test(t, resource.TestCase{ 26 PreCheck: func() { testAccPreCheck(t) }, 27 Providers: testAccProviders, 28 CheckDestroy: testAccCheckLambdaFunctionDestroy, 29 Steps: []resource.TestStep{ 30 resource.TestStep{ 31 Config: testAccAWSLambdaConfigBasic(rName), 32 Check: resource.ComposeTestCheckFunc( 33 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf), 34 testAccCheckAwsLambdaFunctionName(&conf, rName), 35 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName), 36 ), 37 }, 38 }, 39 }) 40 } 41 42 func TestAccAWSLambdaFunction_VPC(t *testing.T) { 43 var conf lambda.GetFunctionOutput 44 rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5)) 45 46 resource.Test(t, resource.TestCase{ 47 PreCheck: func() { testAccPreCheck(t) }, 48 Providers: testAccProviders, 49 CheckDestroy: testAccCheckLambdaFunctionDestroy, 50 Steps: []resource.TestStep{ 51 resource.TestStep{ 52 Config: testAccAWSLambdaConfigWithVPC(rName), 53 Check: resource.ComposeTestCheckFunc( 54 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf), 55 testAccCheckAwsLambdaFunctionName(&conf, rName), 56 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName), 57 testAccCheckAWSLambdaFunctionVersion(&conf, "$LATEST"), 58 resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "vpc_config.#", "1"), 59 resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "vpc_config.0.subnet_ids.#", "1"), 60 resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "vpc_config.0.security_group_ids.#", "1"), 61 resource.TestMatchResourceAttr("aws_lambda_function.lambda_function_test", "vpc_config.0.vpc_id", regexp.MustCompile("^vpc-")), 62 ), 63 }, 64 }, 65 }) 66 } 67 68 func TestAccAWSLambdaFunction_s3(t *testing.T) { 69 var conf lambda.GetFunctionOutput 70 rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5)) 71 72 resource.Test(t, resource.TestCase{ 73 PreCheck: func() { testAccPreCheck(t) }, 74 Providers: testAccProviders, 75 CheckDestroy: testAccCheckLambdaFunctionDestroy, 76 Steps: []resource.TestStep{ 77 resource.TestStep{ 78 Config: testAccAWSLambdaConfigS3(rName), 79 Check: resource.ComposeTestCheckFunc( 80 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3test", rName, &conf), 81 testAccCheckAwsLambdaFunctionName(&conf, rName), 82 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName), 83 testAccCheckAWSLambdaFunctionVersion(&conf, "$LATEST"), 84 ), 85 }, 86 }, 87 }) 88 } 89 90 func TestAccAWSLambdaFunction_localUpdate(t *testing.T) { 91 var conf lambda.GetFunctionOutput 92 93 path, zipFile, err := createTempFile("lambda_localUpdate") 94 if err != nil { 95 t.Fatal(err) 96 } 97 defer os.Remove(path) 98 99 resource.Test(t, resource.TestCase{ 100 PreCheck: func() { testAccPreCheck(t) }, 101 Providers: testAccProviders, 102 CheckDestroy: testAccCheckLambdaFunctionDestroy, 103 Steps: []resource.TestStep{ 104 resource.TestStep{ 105 PreConfig: func() { 106 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile) 107 }, 108 Config: genAWSLambdaFunctionConfig_local(path), 109 Check: resource.ComposeTestCheckFunc( 110 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_local", "tf_acc_lambda_name_local", &conf), 111 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_local"), 112 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_local"), 113 testAccCheckAwsLambdaSourceCodeHash(&conf, "un6qF9S9hKvXbWwJ6m2EYaVCWjcr0PCZWiTV3h4zB0I="), 114 ), 115 }, 116 resource.TestStep{ 117 PreConfig: func() { 118 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, zipFile) 119 }, 120 Config: genAWSLambdaFunctionConfig_local(path), 121 Check: resource.ComposeTestCheckFunc( 122 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_local", "tf_acc_lambda_name_local", &conf), 123 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_local"), 124 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_local"), 125 testAccCheckAwsLambdaSourceCodeHash(&conf, "Y5Jf4Si63UDy1wKNfPs+U56ZL0NxsieKPt9EwRl4GQM="), 126 ), 127 }, 128 }, 129 }) 130 } 131 132 func TestAccAWSLambdaFunction_s3Update(t *testing.T) { 133 var conf lambda.GetFunctionOutput 134 135 path, zipFile, err := createTempFile("lambda_s3Update") 136 if err != nil { 137 t.Fatal(err) 138 } 139 defer os.Remove(path) 140 141 bucketName := fmt.Sprintf("tf-acc-lambda-s3-deployments-%d", randomInteger) 142 key := "lambda-func.zip" 143 144 resource.Test(t, resource.TestCase{ 145 PreCheck: func() { testAccPreCheck(t) }, 146 Providers: testAccProviders, 147 CheckDestroy: testAccCheckLambdaFunctionDestroy, 148 Steps: []resource.TestStep{ 149 resource.TestStep{ 150 PreConfig: func() { 151 // Upload 1st version 152 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile) 153 }, 154 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 155 Check: resource.ComposeTestCheckFunc( 156 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3", "tf_acc_lambda_name_s3", &conf), 157 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_s3"), 158 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_s3"), 159 testAccCheckAwsLambdaSourceCodeHash(&conf, "un6qF9S9hKvXbWwJ6m2EYaVCWjcr0PCZWiTV3h4zB0I="), 160 ), 161 }, 162 resource.TestStep{ 163 ExpectNonEmptyPlan: true, 164 PreConfig: func() { 165 // Upload 2nd version 166 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, zipFile) 167 }, 168 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 169 }, 170 // Extra step because of missing ComputedWhen 171 // See https://github.com/hashicorp/terraform/pull/4846 & https://github.com/hashicorp/terraform/pull/5330 172 resource.TestStep{ 173 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 174 Check: resource.ComposeTestCheckFunc( 175 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3", "tf_acc_lambda_name_s3", &conf), 176 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_s3"), 177 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_s3"), 178 testAccCheckAwsLambdaSourceCodeHash(&conf, "Y5Jf4Si63UDy1wKNfPs+U56ZL0NxsieKPt9EwRl4GQM="), 179 ), 180 }, 181 }, 182 }) 183 } 184 185 func testAccCheckLambdaFunctionDestroy(s *terraform.State) error { 186 conn := testAccProvider.Meta().(*AWSClient).lambdaconn 187 188 for _, rs := range s.RootModule().Resources { 189 if rs.Type != "aws_lambda_function" { 190 continue 191 } 192 193 _, err := conn.GetFunction(&lambda.GetFunctionInput{ 194 FunctionName: aws.String(rs.Primary.ID), 195 }) 196 197 if err == nil { 198 return fmt.Errorf("Lambda Function still exists") 199 } 200 201 } 202 203 return nil 204 205 } 206 207 func testAccCheckAwsLambdaFunctionExists(res, funcName string, function *lambda.GetFunctionOutput) resource.TestCheckFunc { 208 // Wait for IAM role 209 return func(s *terraform.State) error { 210 rs, ok := s.RootModule().Resources[res] 211 if !ok { 212 return fmt.Errorf("Lambda function not found: %s", res) 213 } 214 215 if rs.Primary.ID == "" { 216 return fmt.Errorf("Lambda function ID not set") 217 } 218 219 conn := testAccProvider.Meta().(*AWSClient).lambdaconn 220 221 params := &lambda.GetFunctionInput{ 222 FunctionName: aws.String(funcName), 223 } 224 225 getFunction, err := conn.GetFunction(params) 226 if err != nil { 227 return err 228 } 229 230 *function = *getFunction 231 232 return nil 233 } 234 } 235 236 func testAccCheckAwsLambdaFunctionName(function *lambda.GetFunctionOutput, expectedName string) resource.TestCheckFunc { 237 return func(s *terraform.State) error { 238 c := function.Configuration 239 if *c.FunctionName != expectedName { 240 return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName) 241 } 242 243 return nil 244 } 245 } 246 247 func testAccCheckAWSLambdaFunctionVersion(function *lambda.GetFunctionOutput, expectedVersion string) resource.TestCheckFunc { 248 return func(s *terraform.State) error { 249 c := function.Configuration 250 if *c.Version != expectedVersion { 251 return fmt.Errorf("Expected version %s, got %s", expectedVersion, *c.Version) 252 } 253 return nil 254 } 255 } 256 257 func testAccCheckAwsLambdaFunctionArnHasSuffix(function *lambda.GetFunctionOutput, arnSuffix string) resource.TestCheckFunc { 258 return func(s *terraform.State) error { 259 c := function.Configuration 260 if !strings.HasSuffix(*c.FunctionArn, arnSuffix) { 261 return fmt.Errorf("Expected function ARN %s to have suffix %s", *c.FunctionArn, arnSuffix) 262 } 263 264 return nil 265 } 266 } 267 268 func testAccCheckAwsLambdaSourceCodeHash(function *lambda.GetFunctionOutput, expectedHash string) resource.TestCheckFunc { 269 return func(s *terraform.State) error { 270 c := function.Configuration 271 if *c.CodeSha256 != expectedHash { 272 return fmt.Errorf("Expected code hash %s, got %s", expectedHash, *c.CodeSha256) 273 } 274 275 return nil 276 } 277 } 278 279 func testAccCreateZipFromFiles(files map[string]string, zipFile *os.File) error { 280 zipFile.Truncate(0) 281 zipFile.Seek(0, 0) 282 283 w := zip.NewWriter(zipFile) 284 285 for source, destination := range files { 286 f, err := w.Create(destination) 287 if err != nil { 288 return err 289 } 290 291 fileContent, err := ioutil.ReadFile(source) 292 if err != nil { 293 return err 294 } 295 296 _, err = f.Write(fileContent) 297 if err != nil { 298 return err 299 } 300 } 301 302 err := w.Close() 303 if err != nil { 304 return err 305 } 306 307 return w.Flush() 308 } 309 310 func createTempFile(prefix string) (string, *os.File, error) { 311 f, err := ioutil.TempFile(os.TempDir(), prefix) 312 if err != nil { 313 return "", nil, err 314 } 315 316 pathToFile, err := filepath.Abs(f.Name()) 317 if err != nil { 318 return "", nil, err 319 } 320 return pathToFile, f, nil 321 } 322 323 const baseAccAWSLambdaConfig = ` 324 resource "aws_iam_role_policy" "iam_policy_for_lambda" { 325 name = "iam_policy_for_lambda" 326 role = "${aws_iam_role.iam_for_lambda.id}" 327 policy = <<EOF 328 { 329 "Version": "2012-10-17", 330 "Statement": [ 331 { 332 "Effect": "Allow", 333 "Action": [ 334 "logs:CreateLogGroup", 335 "logs:CreateLogStream", 336 "logs:PutLogEvents" 337 ], 338 "Resource": "arn:aws:logs:*:*:*" 339 }, 340 { 341 "Effect": "Allow", 342 "Action": [ 343 "ec2:CreateNetworkInterface" 344 ], 345 "Resource": [ 346 "*" 347 ] 348 } 349 ] 350 } 351 EOF 352 } 353 354 resource "aws_iam_role" "iam_for_lambda" { 355 name = "iam_for_lambda" 356 assume_role_policy = <<EOF 357 { 358 "Version": "2012-10-17", 359 "Statement": [ 360 { 361 "Action": "sts:AssumeRole", 362 "Principal": { 363 "Service": "lambda.amazonaws.com" 364 }, 365 "Effect": "Allow", 366 "Sid": "" 367 } 368 ] 369 } 370 EOF 371 } 372 373 resource "aws_vpc" "vpc_for_lambda" { 374 cidr_block = "10.0.0.0/16" 375 } 376 377 resource "aws_subnet" "subnet_for_lambda" { 378 vpc_id = "${aws_vpc.vpc_for_lambda.id}" 379 cidr_block = "10.0.1.0/24" 380 381 tags { 382 Name = "lambda" 383 } 384 } 385 386 resource "aws_security_group" "sg_for_lambda" { 387 name = "sg_for_lambda" 388 description = "Allow all inbound traffic for lambda test" 389 vpc_id = "${aws_vpc.vpc_for_lambda.id}" 390 391 ingress { 392 from_port = 0 393 to_port = 0 394 protocol = "-1" 395 cidr_blocks = ["0.0.0.0/0"] 396 } 397 398 egress { 399 from_port = 0 400 to_port = 0 401 protocol = "-1" 402 cidr_blocks = ["0.0.0.0/0"] 403 } 404 } 405 406 ` 407 408 func testAccAWSLambdaConfigBasic(rName string) string { 409 return fmt.Sprintf(baseAccAWSLambdaConfig+` 410 resource "aws_lambda_function" "lambda_function_test" { 411 filename = "test-fixtures/lambdatest.zip" 412 function_name = "%s" 413 role = "${aws_iam_role.iam_for_lambda.arn}" 414 handler = "exports.example" 415 } 416 `, rName) 417 } 418 419 func testAccAWSLambdaConfigWithVPC(rName string) string { 420 return fmt.Sprintf(baseAccAWSLambdaConfig+` 421 resource "aws_lambda_function" "lambda_function_test" { 422 filename = "test-fixtures/lambdatest.zip" 423 function_name = "%s" 424 role = "${aws_iam_role.iam_for_lambda.arn}" 425 handler = "exports.example" 426 427 vpc_config = { 428 subnet_ids = ["${aws_subnet.subnet_for_lambda.id}"] 429 security_group_ids = ["${aws_security_group.sg_for_lambda.id}"] 430 } 431 }`, rName) 432 } 433 434 func testAccAWSLambdaConfigS3(rName string) string { 435 return fmt.Sprintf(` 436 resource "aws_s3_bucket" "lambda_bucket" { 437 bucket = "tf-test-bucket-%d" 438 } 439 440 resource "aws_s3_bucket_object" "lambda_code" { 441 bucket = "${aws_s3_bucket.lambda_bucket.id}" 442 key = "lambdatest.zip" 443 source = "test-fixtures/lambdatest.zip" 444 } 445 446 resource "aws_iam_role" "iam_for_lambda" { 447 name = "iam_for_lambda" 448 assume_role_policy = <<EOF 449 { 450 "Version": "2012-10-17", 451 "Statement": [ 452 { 453 "Action": "sts:AssumeRole", 454 "Principal": { 455 "Service": "lambda.amazonaws.com" 456 }, 457 "Effect": "Allow", 458 "Sid": "" 459 } 460 ] 461 } 462 EOF 463 } 464 465 resource "aws_lambda_function" "lambda_function_s3test" { 466 s3_bucket = "${aws_s3_bucket.lambda_bucket.id}" 467 s3_key = "${aws_s3_bucket_object.lambda_code.id}" 468 function_name = "%s" 469 role = "${aws_iam_role.iam_for_lambda.arn}" 470 handler = "exports.example" 471 } 472 `, acctest.RandInt(), rName) 473 } 474 475 const testAccAWSLambdaFunctionConfig_local_tpl = ` 476 resource "aws_iam_role" "iam_for_lambda" { 477 name = "iam_for_lambda" 478 assume_role_policy = <<EOF 479 { 480 "Version": "2012-10-17", 481 "Statement": [ 482 { 483 "Action": "sts:AssumeRole", 484 "Principal": { 485 "Service": "lambda.amazonaws.com" 486 }, 487 "Effect": "Allow", 488 "Sid": "" 489 } 490 ] 491 } 492 EOF 493 } 494 resource "aws_lambda_function" "lambda_function_local" { 495 filename = "%s" 496 source_code_hash = "${base64sha256(file("%s"))}" 497 function_name = "tf_acc_lambda_name_local" 498 role = "${aws_iam_role.iam_for_lambda.arn}" 499 handler = "exports.example" 500 } 501 ` 502 503 func genAWSLambdaFunctionConfig_local(filePath string) string { 504 return fmt.Sprintf(testAccAWSLambdaFunctionConfig_local_tpl, 505 filePath, filePath) 506 } 507 508 const testAccAWSLambdaFunctionConfig_s3_tpl = ` 509 resource "aws_s3_bucket" "artifacts" { 510 bucket = "%s" 511 acl = "private" 512 force_destroy = true 513 versioning { 514 enabled = true 515 } 516 } 517 resource "aws_s3_bucket_object" "o" { 518 bucket = "${aws_s3_bucket.artifacts.bucket}" 519 key = "%s" 520 source = "%s" 521 etag = "${md5(file("%s"))}" 522 } 523 resource "aws_iam_role" "iam_for_lambda" { 524 name = "iam_for_lambda" 525 assume_role_policy = <<EOF 526 { 527 "Version": "2012-10-17", 528 "Statement": [ 529 { 530 "Action": "sts:AssumeRole", 531 "Principal": { 532 "Service": "lambda.amazonaws.com" 533 }, 534 "Effect": "Allow", 535 "Sid": "" 536 } 537 ] 538 } 539 EOF 540 } 541 resource "aws_lambda_function" "lambda_function_s3" { 542 s3_bucket = "${aws_s3_bucket_object.o.bucket}" 543 s3_key = "${aws_s3_bucket_object.o.key}" 544 s3_object_version = "${aws_s3_bucket_object.o.version_id}" 545 function_name = "tf_acc_lambda_name_s3" 546 role = "${aws_iam_role.iam_for_lambda.arn}" 547 handler = "exports.example" 548 } 549 ` 550 551 func genAWSLambdaFunctionConfig_s3(bucket, key, path string) string { 552 return fmt.Sprintf(testAccAWSLambdaFunctionConfig_s3_tpl, 553 bucket, key, path, path) 554 }