diff --git a/README.md b/README.md index cfcbfcc..f2cf36e 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ This Terraform module creates and uploads an AWS Lambda function and hides the u ## Requirements -* Python -* Linux/Unix +* Python 2.7 or higher +* Linux/Unix/Windows ## Usage diff --git a/archive.tf b/archive.tf index 071a0b3..9f5be61 100644 --- a/archive.tf +++ b/archive.tf @@ -1,7 +1,7 @@ # Generates a filename for the zip archive based on the contents of the files # in source_path. The filename will change when the source code changes. data "external" "archive" { - program = ["${path.module}/hash.py"] + program = ["python", "${path.module}/hash.py"] query = { runtime = "${var.runtime}" @@ -26,7 +26,7 @@ resource "null_resource" "archive" { # deletes the Lambda function. If the file is rebuilt here, the build # output is unfortunately invisible. data "external" "built" { - program = ["${path.module}/built.py"] + program = ["python", "${path.module}/built.py"] query = { build_command = "${lookup(data.external.archive.result, "build_command")}" diff --git a/build.py b/build.py old mode 100755 new mode 100644 index e5e8385..6e1be00 --- a/build.py +++ b/build.py @@ -1,8 +1,5 @@ -#!/usr/bin/env python3 -# # Builds a zip file from the source_dir or source_file. # Installs dependencies with pip automatically. -# import base64 import json @@ -15,15 +12,20 @@ from contextlib import contextmanager +@contextmanager def cd(path): """ Changes the working directory. """ - if os.getcwd() != path: - print('cd', path) + cwd = os.getcwd() + print('cd', path) + try: os.chdir(path) + yield + finally: + os.chdir(cwd) def format_command(command): @@ -96,8 +98,12 @@ def create_zip_file(source_dir, target_file): target_dir = os.path.dirname(target_file) if not os.path.exists(target_dir): os.makedirs(target_dir) - cd(source_dir) - run('zip', '-r', target_file, '.') + target_base, _ = os.path.splitext(target_file) + shutil.make_archive( + target_base, + format='zip', + root_dir=source_dir, + ) json_payload = bytes.decode(base64.b64decode(sys.argv[1])) query = json.loads(json_payload) @@ -120,26 +126,26 @@ def create_zip_file(source_dir, target_file): source_files = [os.path.basename(source_path)] # Copy them into the temporary directory. - cd(source_dir) - for file_name in source_files: - target_path = os.path.join(temp_dir, file_name) - target_dir = os.path.dirname(target_path) - if not os.path.exists(target_dir): - print('mkdir -p {}'.format(target_dir)) - os.makedirs(target_dir) - print('cp {} {}'.format(file_name, target_path)) - shutil.copyfile(file_name, target_path) + with cd(source_dir): + for file_name in source_files: + target_path = os.path.join(temp_dir, file_name) + target_dir = os.path.dirname(target_path) + if not os.path.exists(target_dir): + print('mkdir -p {}'.format(target_dir)) + os.makedirs(target_dir) + print('cp {} {}'.format(file_name, target_path)) + shutil.copyfile(file_name, target_path) # Install dependencies into the temporary directory. if runtime.startswith('python'): requirements = os.path.join(temp_dir, 'requirements.txt') if os.path.exists(requirements): - cd(temp_dir) - if runtime.startswith('python3'): - pip_command = 'pip3' - else: - pip_command = 'pip2' - run(pip_command, 'install', '-r', 'requirements.txt', '-t', '.') + with cd(temp_dir): + if runtime.startswith('python3'): + pip_command = 'pip3' + else: + pip_command = 'pip2' + run(pip_command, 'install', '-r', 'requirements.txt', '-t', '.') # Zip up the temporary directory and write it to the target filename. # This will be used by the Lambda function as the source code package. diff --git a/built.py b/built.py old mode 100755 new mode 100644 index deb128f..6f96061 --- a/built.py +++ b/built.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +# Builds missing zip files included in the +# Terraform state but missing from the disk. import json import os diff --git a/hash.py b/hash.py old mode 100755 new mode 100644 index 8e2beb4..1f9bf44 --- a/hash.py +++ b/hash.py @@ -1,10 +1,7 @@ -#!/usr/bin/env python3 -# # Generates a content hash of the source_path, which is used to determine if # the Lambda code has changed, ignoring file modification and access times. # # Outputs a filename and a command to run if the archive needs to be built. -# import base64 import datetime @@ -145,7 +142,7 @@ def update_hash(hash_obj, file_root, file_path): ) # Determine the command to run if Terraform wants to build a new archive. -build_command = "{build_script} {build_data}".format( +build_command = "python {build_script} {build_data}".format( build_script=os.path.join(current_dir, 'build.py'), build_data=bytes.decode(base64.b64encode(str.encode( json.dumps({ diff --git a/tests/python3-pip/lambda/__init__.py b/tests/python-versions/lambda/__init__.py similarity index 100% rename from tests/python3-pip/lambda/__init__.py rename to tests/python-versions/lambda/__init__.py diff --git a/tests/python3-pip/lambda/main.py b/tests/python-versions/lambda/main.py similarity index 100% rename from tests/python3-pip/lambda/main.py rename to tests/python-versions/lambda/main.py diff --git a/tests/python3-pip/lambda/requirements.txt b/tests/python-versions/lambda/requirements.txt similarity index 100% rename from tests/python3-pip/lambda/requirements.txt rename to tests/python-versions/lambda/requirements.txt diff --git a/tests/python3-pip/lambda/test_result/__init__.py b/tests/python-versions/lambda/test_result/__init__.py similarity index 100% rename from tests/python3-pip/lambda/test_result/__init__.py rename to tests/python-versions/lambda/test_result/__init__.py diff --git a/tests/python-versions/python2/.envrc b/tests/python-versions/python2/.envrc new file mode 100644 index 0000000..7969e54 --- /dev/null +++ b/tests/python-versions/python2/.envrc @@ -0,0 +1 @@ +layout python2 diff --git a/tests/python-versions/python2/main.tf b/tests/python-versions/python2/main.tf new file mode 100644 index 0000000..7e611c9 --- /dev/null +++ b/tests/python-versions/python2/main.tf @@ -0,0 +1,33 @@ +terraform { + backend "local" { + path = "terraform.tfstate" + } +} + +provider "aws" { + region = "eu-west-1" +} + +module "lambda_python2" { + source = "../../../" + + function_name = "terraform-aws-lambda-test-python2-from-python2" + description = "Test python2 runtime from python2 environment in terraform-aws-lambda" + handler = "main.lambda_handler" + runtime = "python2.7" + timeout = 5 + + source_path = "${path.module}/../lambda" +} + +module "lambda_python3" { + source = "../../../" + + function_name = "terraform-aws-lambda-test-python3-from-python2" + description = "Test python3 runtime from python2 environment in terraform-aws-lambda" + handler = "main.lambda_handler" + runtime = "python3.7" + timeout = 5 + + source_path = "${path.module}/../lambda" +} diff --git a/tests/python-versions/python3/.envrc b/tests/python-versions/python3/.envrc new file mode 100644 index 0000000..94840b3 --- /dev/null +++ b/tests/python-versions/python3/.envrc @@ -0,0 +1 @@ +layout python3 diff --git a/tests/python-versions/python3/main.tf b/tests/python-versions/python3/main.tf new file mode 100644 index 0000000..39f36d7 --- /dev/null +++ b/tests/python-versions/python3/main.tf @@ -0,0 +1,33 @@ +terraform { + backend "local" { + path = "terraform.tfstate" + } +} + +provider "aws" { + region = "eu-west-1" +} + +module "lambda_python2" { + source = "../../../" + + function_name = "terraform-aws-lambda-test-python2-from-python3" + description = "Test python2 runtime from python3 environment in terraform-aws-lambda" + handler = "main.lambda_handler" + runtime = "python2.7" + timeout = 5 + + source_path = "${path.module}/../lambda" +} + +module "lambda_python3" { + source = "../../../" + + function_name = "terraform-aws-lambda-test-python3-from-python3" + description = "Test python3 runtime from python3 environment in terraform-aws-lambda" + handler = "main.lambda_handler" + runtime = "python3.7" + timeout = 5 + + source_path = "${path.module}/../lambda" +} diff --git a/tests/python3-pip/main.tf b/tests/python3-pip/main.tf deleted file mode 100644 index d82b955..0000000 --- a/tests/python3-pip/main.tf +++ /dev/null @@ -1,21 +0,0 @@ -terraform { - backend "local" { - path = "terraform.tfstate" - } -} - -provider "aws" { - region = "eu-west-1" -} - -module "lambda" { - source = "../../" - - function_name = "terraform-aws-lambda-test-python3-pip" - description = "Test python3 pip functionality in terraform-aws-lambda" - handler = "main.lambda_handler" - runtime = "python3.6" - timeout = 30 - - source_path = "${path.module}/lambda" -}