github.com/memsql/terraform@v0.7.0-rc2.0.20160706152241-21e2173e0a32/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_localUpdate_nameOnly(t *testing.T) { 133 var conf lambda.GetFunctionOutput 134 135 path, zipFile, err := createTempFile("lambda_localUpdate") 136 if err != nil { 137 t.Fatal(err) 138 } 139 defer os.Remove(path) 140 141 updatedPath, updatedZipFile, err := createTempFile("lambda_localUpdate_name_change") 142 if err != nil { 143 t.Fatal(err) 144 } 145 defer os.Remove(updatedPath) 146 147 resource.Test(t, resource.TestCase{ 148 PreCheck: func() { testAccPreCheck(t) }, 149 Providers: testAccProviders, 150 CheckDestroy: testAccCheckLambdaFunctionDestroy, 151 Steps: []resource.TestStep{ 152 resource.TestStep{ 153 PreConfig: func() { 154 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile) 155 }, 156 Config: genAWSLambdaFunctionConfig_local_name_only(path), 157 Check: resource.ComposeTestCheckFunc( 158 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_local", "tf_acc_lambda_name_local", &conf), 159 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_local"), 160 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_local"), 161 testAccCheckAwsLambdaSourceCodeHash(&conf, "un6qF9S9hKvXbWwJ6m2EYaVCWjcr0PCZWiTV3h4zB0I="), 162 ), 163 }, 164 resource.TestStep{ 165 PreConfig: func() { 166 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, updatedZipFile) 167 }, 168 Config: genAWSLambdaFunctionConfig_local_name_only(updatedPath), 169 Check: resource.ComposeTestCheckFunc( 170 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_local", "tf_acc_lambda_name_local", &conf), 171 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_local"), 172 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_local"), 173 testAccCheckAwsLambdaSourceCodeHash(&conf, "Y5Jf4Si63UDy1wKNfPs+U56ZL0NxsieKPt9EwRl4GQM="), 174 ), 175 }, 176 }, 177 }) 178 } 179 180 func TestAccAWSLambdaFunction_s3Update(t *testing.T) { 181 var conf lambda.GetFunctionOutput 182 183 path, zipFile, err := createTempFile("lambda_s3Update") 184 if err != nil { 185 t.Fatal(err) 186 } 187 defer os.Remove(path) 188 189 bucketName := fmt.Sprintf("tf-acc-lambda-s3-deployments-%d", randomInteger) 190 key := "lambda-func.zip" 191 192 resource.Test(t, resource.TestCase{ 193 PreCheck: func() { testAccPreCheck(t) }, 194 Providers: testAccProviders, 195 CheckDestroy: testAccCheckLambdaFunctionDestroy, 196 Steps: []resource.TestStep{ 197 resource.TestStep{ 198 PreConfig: func() { 199 // Upload 1st version 200 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile) 201 }, 202 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 203 Check: resource.ComposeTestCheckFunc( 204 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3", "tf_acc_lambda_name_s3", &conf), 205 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_s3"), 206 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_s3"), 207 testAccCheckAwsLambdaSourceCodeHash(&conf, "un6qF9S9hKvXbWwJ6m2EYaVCWjcr0PCZWiTV3h4zB0I="), 208 ), 209 }, 210 resource.TestStep{ 211 ExpectNonEmptyPlan: true, 212 PreConfig: func() { 213 // Upload 2nd version 214 testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, zipFile) 215 }, 216 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 217 }, 218 // Extra step because of missing ComputedWhen 219 // See https://github.com/hashicorp/terraform/pull/4846 & https://github.com/hashicorp/terraform/pull/5330 220 resource.TestStep{ 221 Config: genAWSLambdaFunctionConfig_s3(bucketName, key, path), 222 Check: resource.ComposeTestCheckFunc( 223 testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3", "tf_acc_lambda_name_s3", &conf), 224 testAccCheckAwsLambdaFunctionName(&conf, "tf_acc_lambda_name_s3"), 225 testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, "tf_acc_lambda_name_s3"), 226 testAccCheckAwsLambdaSourceCodeHash(&conf, "Y5Jf4Si63UDy1wKNfPs+U56ZL0NxsieKPt9EwRl4GQM="), 227 ), 228 }, 229 }, 230 }) 231 } 232 233 func testAccCheckLambdaFunctionDestroy(s *terraform.State) error { 234 conn := testAccProvider.Meta().(*AWSClient).lambdaconn 235 236 for _, rs := range s.RootModule().Resources { 237 if rs.Type != "aws_lambda_function" { 238 continue 239 } 240 241 _, err := conn.GetFunction(&lambda.GetFunctionInput{ 242 FunctionName: aws.String(rs.Primary.ID), 243 }) 244 245 if err == nil { 246 return fmt.Errorf("Lambda Function still exists") 247 } 248 249 } 250 251 return nil 252 253 } 254 255 func testAccCheckAwsLambdaFunctionExists(res, funcName string, function *lambda.GetFunctionOutput) resource.TestCheckFunc { 256 // Wait for IAM role 257 return func(s *terraform.State) error { 258 rs, ok := s.RootModule().Resources[res] 259 if !ok { 260 return fmt.Errorf("Lambda function not found: %s", res) 261 } 262 263 if rs.Primary.ID == "" { 264 return fmt.Errorf("Lambda function ID not set") 265 } 266 267 conn := testAccProvider.Meta().(*AWSClient).lambdaconn 268 269 params := &lambda.GetFunctionInput{ 270 FunctionName: aws.String(funcName), 271 } 272 273 getFunction, err := conn.GetFunction(params) 274 if err != nil { 275 return err 276 } 277 278 *function = *getFunction 279 280 return nil 281 } 282 } 283 284 func testAccCheckAwsLambdaFunctionName(function *lambda.GetFunctionOutput, expectedName string) resource.TestCheckFunc { 285 return func(s *terraform.State) error { 286 c := function.Configuration 287 if *c.FunctionName != expectedName { 288 return fmt.Errorf("Expected function name %s, got %s", expectedName, *c.FunctionName) 289 } 290 291 return nil 292 } 293 } 294 295 func testAccCheckAWSLambdaFunctionVersion(function *lambda.GetFunctionOutput, expectedVersion string) resource.TestCheckFunc { 296 return func(s *terraform.State) error { 297 c := function.Configuration 298 if *c.Version != expectedVersion { 299 return fmt.Errorf("Expected version %s, got %s", expectedVersion, *c.Version) 300 } 301 return nil 302 } 303 } 304 305 func testAccCheckAwsLambdaFunctionArnHasSuffix(function *lambda.GetFunctionOutput, arnSuffix string) resource.TestCheckFunc { 306 return func(s *terraform.State) error { 307 c := function.Configuration 308 if !strings.HasSuffix(*c.FunctionArn, arnSuffix) { 309 return fmt.Errorf("Expected function ARN %s to have suffix %s", *c.FunctionArn, arnSuffix) 310 } 311 312 return nil 313 } 314 } 315 316 func testAccCheckAwsLambdaSourceCodeHash(function *lambda.GetFunctionOutput, expectedHash string) resource.TestCheckFunc { 317 return func(s *terraform.State) error { 318 c := function.Configuration 319 if *c.CodeSha256 != expectedHash { 320 return fmt.Errorf("Expected code hash %s, got %s", expectedHash, *c.CodeSha256) 321 } 322 323 return nil 324 } 325 } 326 327 func testAccCreateZipFromFiles(files map[string]string, zipFile *os.File) error { 328 zipFile.Truncate(0) 329 zipFile.Seek(0, 0) 330 331 w := zip.NewWriter(zipFile) 332 333 for source, destination := range files { 334 f, err := w.Create(destination) 335 if err != nil { 336 return err 337 } 338 339 fileContent, err := ioutil.ReadFile(source) 340 if err != nil { 341 return err 342 } 343 344 _, err = f.Write(fileContent) 345 if err != nil { 346 return err 347 } 348 } 349 350 err := w.Close() 351 if err != nil { 352 return err 353 } 354 355 return w.Flush() 356 } 357 358 func createTempFile(prefix string) (string, *os.File, error) { 359 f, err := ioutil.TempFile(os.TempDir(), prefix) 360 if err != nil { 361 return "", nil, err 362 } 363 364 pathToFile, err := filepath.Abs(f.Name()) 365 if err != nil { 366 return "", nil, err 367 } 368 return pathToFile, f, nil 369 } 370 371 const baseAccAWSLambdaConfig = ` 372 resource "aws_iam_role_policy" "iam_policy_for_lambda" { 373 name = "iam_policy_for_lambda" 374 role = "${aws_iam_role.iam_for_lambda.id}" 375 policy = <<EOF 376 { 377 "Version": "2012-10-17", 378 "Statement": [ 379 { 380 "Effect": "Allow", 381 "Action": [ 382 "logs:CreateLogGroup", 383 "logs:CreateLogStream", 384 "logs:PutLogEvents" 385 ], 386 "Resource": "arn:aws:logs:*:*:*" 387 }, 388 { 389 "Effect": "Allow", 390 "Action": [ 391 "ec2:CreateNetworkInterface" 392 ], 393 "Resource": [ 394 "*" 395 ] 396 } 397 ] 398 } 399 EOF 400 } 401 402 resource "aws_iam_role" "iam_for_lambda" { 403 name = "iam_for_lambda" 404 assume_role_policy = <<EOF 405 { 406 "Version": "2012-10-17", 407 "Statement": [ 408 { 409 "Action": "sts:AssumeRole", 410 "Principal": { 411 "Service": "lambda.amazonaws.com" 412 }, 413 "Effect": "Allow", 414 "Sid": "" 415 } 416 ] 417 } 418 EOF 419 } 420 421 resource "aws_vpc" "vpc_for_lambda" { 422 cidr_block = "10.0.0.0/16" 423 } 424 425 resource "aws_subnet" "subnet_for_lambda" { 426 vpc_id = "${aws_vpc.vpc_for_lambda.id}" 427 cidr_block = "10.0.1.0/24" 428 429 tags { 430 Name = "lambda" 431 } 432 } 433 434 resource "aws_security_group" "sg_for_lambda" { 435 name = "sg_for_lambda" 436 description = "Allow all inbound traffic for lambda test" 437 vpc_id = "${aws_vpc.vpc_for_lambda.id}" 438 439 ingress { 440 from_port = 0 441 to_port = 0 442 protocol = "-1" 443 cidr_blocks = ["0.0.0.0/0"] 444 } 445 446 egress { 447 from_port = 0 448 to_port = 0 449 protocol = "-1" 450 cidr_blocks = ["0.0.0.0/0"] 451 } 452 } 453 454 ` 455 456 func testAccAWSLambdaConfigBasic(rName string) string { 457 return fmt.Sprintf(baseAccAWSLambdaConfig+` 458 resource "aws_lambda_function" "lambda_function_test" { 459 filename = "test-fixtures/lambdatest.zip" 460 function_name = "%s" 461 role = "${aws_iam_role.iam_for_lambda.arn}" 462 handler = "exports.example" 463 } 464 `, rName) 465 } 466 467 func testAccAWSLambdaConfigWithVPC(rName string) string { 468 return fmt.Sprintf(baseAccAWSLambdaConfig+` 469 resource "aws_lambda_function" "lambda_function_test" { 470 filename = "test-fixtures/lambdatest.zip" 471 function_name = "%s" 472 role = "${aws_iam_role.iam_for_lambda.arn}" 473 handler = "exports.example" 474 475 vpc_config = { 476 subnet_ids = ["${aws_subnet.subnet_for_lambda.id}"] 477 security_group_ids = ["${aws_security_group.sg_for_lambda.id}"] 478 } 479 }`, rName) 480 } 481 482 func testAccAWSLambdaConfigS3(rName string) string { 483 return fmt.Sprintf(` 484 resource "aws_s3_bucket" "lambda_bucket" { 485 bucket = "tf-test-bucket-%d" 486 } 487 488 resource "aws_s3_bucket_object" "lambda_code" { 489 bucket = "${aws_s3_bucket.lambda_bucket.id}" 490 key = "lambdatest.zip" 491 source = "test-fixtures/lambdatest.zip" 492 } 493 494 resource "aws_iam_role" "iam_for_lambda" { 495 name = "iam_for_lambda" 496 assume_role_policy = <<EOF 497 { 498 "Version": "2012-10-17", 499 "Statement": [ 500 { 501 "Action": "sts:AssumeRole", 502 "Principal": { 503 "Service": "lambda.amazonaws.com" 504 }, 505 "Effect": "Allow", 506 "Sid": "" 507 } 508 ] 509 } 510 EOF 511 } 512 513 resource "aws_lambda_function" "lambda_function_s3test" { 514 s3_bucket = "${aws_s3_bucket.lambda_bucket.id}" 515 s3_key = "${aws_s3_bucket_object.lambda_code.id}" 516 function_name = "%s" 517 role = "${aws_iam_role.iam_for_lambda.arn}" 518 handler = "exports.example" 519 } 520 `, acctest.RandInt(), rName) 521 } 522 523 const testAccAWSLambdaFunctionConfig_local_tpl = ` 524 resource "aws_iam_role" "iam_for_lambda" { 525 name = "iam_for_lambda" 526 assume_role_policy = <<EOF 527 { 528 "Version": "2012-10-17", 529 "Statement": [ 530 { 531 "Action": "sts:AssumeRole", 532 "Principal": { 533 "Service": "lambda.amazonaws.com" 534 }, 535 "Effect": "Allow", 536 "Sid": "" 537 } 538 ] 539 } 540 EOF 541 } 542 resource "aws_lambda_function" "lambda_function_local" { 543 filename = "%s" 544 source_code_hash = "${base64sha256(file("%s"))}" 545 function_name = "tf_acc_lambda_name_local" 546 role = "${aws_iam_role.iam_for_lambda.arn}" 547 handler = "exports.example" 548 } 549 ` 550 551 func genAWSLambdaFunctionConfig_local(filePath string) string { 552 return fmt.Sprintf(testAccAWSLambdaFunctionConfig_local_tpl, 553 filePath, filePath) 554 } 555 556 func genAWSLambdaFunctionConfig_local_name_only(filePath string) string { 557 return fmt.Sprintf(testAccAWSLambdaFunctionConfig_local_name_only_tpl, 558 filePath) 559 } 560 561 const testAccAWSLambdaFunctionConfig_local_name_only_tpl = ` 562 resource "aws_iam_role" "iam_for_lambda" { 563 name = "iam_for_lambda" 564 assume_role_policy = <<EOF 565 { 566 "Version": "2012-10-17", 567 "Statement": [ 568 { 569 "Action": "sts:AssumeRole", 570 "Principal": { 571 "Service": "lambda.amazonaws.com" 572 }, 573 "Effect": "Allow", 574 "Sid": "" 575 } 576 ] 577 } 578 EOF 579 } 580 resource "aws_lambda_function" "lambda_function_local" { 581 filename = "%s" 582 function_name = "tf_acc_lambda_name_local" 583 role = "${aws_iam_role.iam_for_lambda.arn}" 584 handler = "exports.example" 585 } 586 ` 587 588 const testAccAWSLambdaFunctionConfig_s3_tpl = ` 589 resource "aws_s3_bucket" "artifacts" { 590 bucket = "%s" 591 acl = "private" 592 force_destroy = true 593 versioning { 594 enabled = true 595 } 596 } 597 resource "aws_s3_bucket_object" "o" { 598 bucket = "${aws_s3_bucket.artifacts.bucket}" 599 key = "%s" 600 source = "%s" 601 etag = "${md5(file("%s"))}" 602 } 603 resource "aws_iam_role" "iam_for_lambda" { 604 name = "iam_for_lambda" 605 assume_role_policy = <<EOF 606 { 607 "Version": "2012-10-17", 608 "Statement": [ 609 { 610 "Action": "sts:AssumeRole", 611 "Principal": { 612 "Service": "lambda.amazonaws.com" 613 }, 614 "Effect": "Allow", 615 "Sid": "" 616 } 617 ] 618 } 619 EOF 620 } 621 resource "aws_lambda_function" "lambda_function_s3" { 622 s3_bucket = "${aws_s3_bucket_object.o.bucket}" 623 s3_key = "${aws_s3_bucket_object.o.key}" 624 s3_object_version = "${aws_s3_bucket_object.o.version_id}" 625 function_name = "tf_acc_lambda_name_s3" 626 role = "${aws_iam_role.iam_for_lambda.arn}" 627 handler = "exports.example" 628 } 629 ` 630 631 func genAWSLambdaFunctionConfig_s3(bucket, key, path string) string { 632 return fmt.Sprintf(testAccAWSLambdaFunctionConfig_s3_tpl, 633 bucket, key, path, path) 634 }