diff --git a/appveyor-windows-build-java-inprocess.yml b/appveyor-windows-build-java-inprocess.yml index 8432ee4218..c15c810b90 100644 --- a/appveyor-windows-build-java-inprocess.yml +++ b/appveyor-windows-build-java-inprocess.yml @@ -55,4 +55,6 @@ install: test_script: # Reactivate virtualenv before running tests - "venv\\Scripts\\activate" - - "pytest -vv tests/integration/buildcmd/test_build_cmd.py -k test_building_java_in_process" + - "pytest -vv tests/integration/buildcmd/test_build_cmd.py -k test_building_java8_in_process" + - "SET JAVA_HOME=C:\\Program Files\\Java\\jdk11" + - "pytest -vv tests/integration/buildcmd/test_build_cmd.py -k test_building_java11_in_process" diff --git a/appveyor-windows-build-python.yml b/appveyor-windows-build-python.yml index a20d9b06ce..7e83ff75ee 100644 --- a/appveyor-windows-build-python.yml +++ b/appveyor-windows-build-python.yml @@ -37,8 +37,13 @@ init: install: # Make sure the temp directory exists for Python to use. + # Install python3.8 + - "choco install python3 --version 3.8.0" + - "C:\\Python38\\python.exe -m pip freeze" + - "refreshenv" + - ps: "mkdir -Force D:\\tmp" - - "SET PATH=%PYTHON_HOME%;%PATH%" + - "SET PATH=%PYTHON_HOME%;%PATH%;C:\\Python37-x64;C:\\Python27-x64;C:\\Python38" - "echo %PYTHON_HOME%" - "echo %PATH%" - "python --version" diff --git a/appveyor.yml b/appveyor.yml index f464399ad2..5bd7850166 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,12 +13,14 @@ environment: PYTHON_VERSION: '3.6.8' PYTHON_ARCH: '64' NOSE_PARAMETERIZED_NO_WARN: 1 + INSTALL_PY_37_PIP: 1 - PYTHON_HOME: "C:\\Python37-x64" PYTHON_VERSION: '3.7.4' PYTHON_ARCH: '64' RUN_SMOKE: 1 NOSE_PARAMETERIZED_NO_WARN: 1 + INSTALL_PY_36_PIP: 1 for: - @@ -98,6 +100,25 @@ for: - sh: "./aws_cli/bin/python -m pip install awscli" - sh: "PATH=$(echo $PWD'/aws_cli/bin'):$PATH" + - sh: "sudo apt-get -y install python3.6" + - sh: "sudo apt-get -y install python2.7" + - sh: "sudo apt-get -y install python3.7" + - sh: "sudo apt-get update" + - sh: "sudo apt-get -y install python3.8" + + - sh: "which python3.8" + - sh: "which python3.6" + - sh: "which python3.7" + - sh: "which python2.7" + + - sh: "PATH=$PATH:/usr/bin/python3.8:/usr/bin/python3.7" + - sh: "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" + + - sh: "sudo apt-get -y install python3-distutils" + - sh: "python3.8 get-pip.py --user" + - ps: "If ($env:INSTALL_PY_37_PIP) {python3.7 get-pip.py --user}" + - ps: "If ($env:INSTALL_PY_36_PIP) {python3.6 get-pip.py --user}" + build_script: - "python -c \"import sys; print(sys.executable)\"" - "pip install -e \".[dev]\"" @@ -113,7 +134,10 @@ for: - sh: "pytest -vv tests/integration" - sh: "pytest -vv -n 4 tests/regression" - sh: "/tmp/black --check setup.py tests samcli scripts" - - sh: "python scripts/check-isolated-needs-update.py" + + # Set JAVA_HOME to java11 + - sh: "JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" + - sh: "pytest -vv tests/integration/buildcmd/test_build_cmd.py -k test_building_java11_in_process" # Smoke tests run in parallel - it runs on both Linux & Windows # Presence of the RUN_SMOKE envvar will run the smoke tests diff --git a/requirements/base.txt b/requirements/base.txt index f6ad4daebe..8cd89436f2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -11,6 +11,6 @@ dateparser~=0.7 python-dateutil~=2.6, <2.8.1 requests==2.22.0 serverlessrepo==0.1.9 -aws_lambda_builders==0.5.0 +aws_lambda_builders==0.6.0 # https://github.com/mhammond/pywin32/issues/1439 pywin32 < 226; sys_platform == 'win32' \ No newline at end of file diff --git a/samcli/__init__.py b/samcli/__init__.py index 6d6932a165..a74a1acfe7 100644 --- a/samcli/__init__.py +++ b/samcli/__init__.py @@ -2,4 +2,4 @@ SAM CLI version """ -__version__ = "0.31.1" +__version__ = "0.32.0" diff --git a/samcli/commands/init/__init__.py b/samcli/commands/init/__init__.py index 23c4537fb9..798e341366 100644 --- a/samcli/commands/init/__init__.py +++ b/samcli/commands/init/__init__.py @@ -90,7 +90,7 @@ help="Disable Cookiecutter prompting and accept default values defined template config", ) @click.option( - "--extra_context", + "--extra-context", default=None, help="Override any custom parameters in the template's cookiecutter.json configuration e.g. " "" diff --git a/samcli/lib/build/workflow_config.py b/samcli/lib/build/workflow_config.py index d649b80375..fa9e521374 100644 --- a/samcli/lib/build/workflow_config.py +++ b/samcli/lib/build/workflow_config.py @@ -95,10 +95,12 @@ def get_workflow_config(runtime, code_dir, project_dir): "python2.7": BasicWorkflowSelector(PYTHON_PIP_CONFIG), "python3.6": BasicWorkflowSelector(PYTHON_PIP_CONFIG), "python3.7": BasicWorkflowSelector(PYTHON_PIP_CONFIG), + "python3.8": BasicWorkflowSelector(PYTHON_PIP_CONFIG), "nodejs4.3": BasicWorkflowSelector(NODEJS_NPM_CONFIG), "nodejs6.10": BasicWorkflowSelector(NODEJS_NPM_CONFIG), "nodejs8.10": BasicWorkflowSelector(NODEJS_NPM_CONFIG), "nodejs10.x": BasicWorkflowSelector(NODEJS_NPM_CONFIG), + "nodejs12.x": BasicWorkflowSelector(NODEJS_NPM_CONFIG), "ruby2.5": BasicWorkflowSelector(RUBY_BUNDLER_CONFIG), "dotnetcore2.0": BasicWorkflowSelector(DOTNET_CLIPACKAGE_CONFIG), "dotnetcore2.1": BasicWorkflowSelector(DOTNET_CLIPACKAGE_CONFIG), @@ -111,6 +113,12 @@ def get_workflow_config(runtime, code_dir, project_dir): JAVA_KOTLIN_GRADLE_CONFIG._replace(executable_search_paths=[code_dir, project_dir]), JAVA_MAVEN_CONFIG ]), + "java11": ManifestWorkflowSelector([ + # Gradle builder needs custom executable paths to find `gradlew` binary + JAVA_GRADLE_CONFIG._replace(executable_search_paths=[code_dir, project_dir]), + JAVA_KOTLIN_GRADLE_CONFIG._replace(executable_search_paths=[code_dir, project_dir]), + JAVA_MAVEN_CONFIG + ]), } if runtime not in selectors_by_runtime: diff --git a/samcli/local/common/runtime_template.py b/samcli/local/common/runtime_template.py index 7788e2cf53..d681b99005 100644 --- a/samcli/local/common/runtime_template.py +++ b/samcli/local/common/runtime_template.py @@ -14,7 +14,7 @@ RUNTIME_DEP_TEMPLATE_MAPPING = { "python": [ { - "runtimes": ["python3.7", "python3.6", "python2.7"], + "runtimes": ["python3.8", "python3.7", "python3.6", "python2.7"], "dependency_manager": "pip", "init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-python"), "build": True, @@ -30,7 +30,7 @@ ], "nodejs": [ { - "runtimes": ["nodejs10.x", "nodejs8.10"], + "runtimes": ["nodejs12.x", "nodejs10.x", "nodejs8.10"], "dependency_manager": "npm", "init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-nodejs"), "build": True, @@ -60,13 +60,13 @@ ], "java": [ { - "runtimes": ["java8"], + "runtimes": ["java11", "java8"], "dependency_manager": "maven", "init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-java-maven"), "build": True, }, { - "runtimes": ["java8"], + "runtimes": ["java11", "java8"], "dependency_manager": "gradle", "init_location": os.path.join(_templates, "cookiecutter-aws-sam-hello-java-gradle"), "build": True, @@ -75,10 +75,12 @@ } RUNTIME_TO_DEPENDENCY_MANAGERS = { + "python3.8": ["pip"], "python3.7": ["pip"], "python3.6": ["pip"], "python2.7": ["pip"], "ruby2.5": ["bundler"], + "nodejs12.x": ["npm"], "nodejs10.x": ["npm"], "nodejs8.10": ["npm"], "nodejs6.10": ["npm"], @@ -87,6 +89,7 @@ "dotnetcore1.0": ["cli-package"], "go1.x": ["mod"], "java8": ["maven", "gradle"], + "java11": ["maven", "gradle"], } SUPPORTED_DEP_MANAGERS = { @@ -99,17 +102,27 @@ itertools.chain(*[c["runtimes"] for c in list(itertools.chain(*(RUNTIME_DEP_TEMPLATE_MAPPING.values())))]) ) +# Order here should be a the group of the latest versions of runtimes followed by runtime groups INIT_RUNTIMES = [ - "nodejs10.x", - "python3.7", + # latest of each runtime version + "nodejs12.x", + "python3.8", "ruby2.5", "go1.x", - "java8", + "java11", "dotnetcore2.1", + # older nodejs runtimes + "nodejs10.x", "nodejs8.10", "nodejs6.10", + # older python runtimes + "python3.7", "python3.6", "python2.7", + # older ruby runtimes + # older java runtimes + "java8", + # older dotnetcore runtimes "dotnetcore2.0", "dotnetcore1.0", ] diff --git a/samcli/local/docker/attach_api.py b/samcli/local/docker/attach_api.py deleted file mode 100644 index 8a46a5268e..0000000000 --- a/samcli/local/docker/attach_api.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -Wrapper to Docker Attach API -""" - -import struct -import logging -from socket import timeout -from docker.utils.socket import read, read_exactly, SocketError - -LOG = logging.getLogger(__name__) - - -def attach(docker_client, container, stdout=True, stderr=True, logs=False): - """ - - Implements a method that wraps Docker Attach API to attach to a container and demux stdout and stderr data from the - single data stream that Docker API returns. - - The signature of this method is intentionally similar to Docker Python SDK's ``attach()`` method except for the - addition of one parameter called `demux` which, if set to True, will return an iterator that provides the stream - data along with a stream type identifier. Caller can handle the data appropriately. - - Parameters - ---------- - docker_client : docker.Client - Docker client used to talk to Docker daemon - - container : docker.container - Instance of the container to attach to - - stdout : bool - Do you want to get stdout data? - - stderr : bool - Do you want to get stderr data? - - logs : bool - Do you want to include the container's previous output? - """ - - headers = {"Connection": "Upgrade", "Upgrade": "tcp"} - - query_params = { - "stdout": 1 if stdout else 0, - "stderr": 1 if stderr else 0, - "logs": 1 if logs else 0, - "stream": 1, # Yes, we always stream - "stdin": 0, # We don't support stdin - } - - # API client is a lower level Docker client that wraps the Docker APIs. It has methods that will help us - # talk to the API directly. It is an instance of ``docker.APIClient``. We are going to use private methods of - # class here because it is sometimes more convenient. - api_client = docker_client.api - - # URL where the Docker daemon is running - url = "{}/containers/{}/attach".format(api_client.base_url, container.id) - - # Send out the attach request and read the socket for response - response = api_client._post(url, headers=headers, params=query_params, stream=True) # pylint: disable=W0212 - socket = api_client._get_raw_response_socket(response) # pylint: disable=W0212 - - return _read_socket(socket) - - -def _read_socket(socket): - """ - The stdout and stderr data from the container multiplexed into one stream of response from the Docker API. - It follows the protocol described here https://docs.docker.com/engine/api/v1.30/#operation/ContainerAttach. - The stream starts with a 8 byte header that contains the frame type and also payload size. Follwing that is the - actual payload of given size. Once you read off this payload, we are ready to read the next header. - - This method will follow this protocol to read payload from the stream and return an iterator that returns - a tuple containing the frame type and frame data. Callers can handle the data appropriately based on the frame - type. - - Stdout => Frame Type = 1 - Stderr => Frame Type = 2 - - - Parameters - ---------- - socket - Socket to read responses from - - Yields - ------- - int - Type of the stream (1 => stdout, 2 => stderr) - str - Data in the stream - """ - - # Keep reading the stream until the stream terminates - while True: - - try: - - payload_type, payload_size = _read_header(socket) - if payload_size < 0: - # Something is wrong with the data stream. Payload size can't be less than zero - break - - for data in _read_payload(socket, payload_size): - yield payload_type, data - - except timeout: - # Timeouts are normal during debug sessions and long running tasks - LOG.debug("Ignoring docker socket timeout") - - except SocketError: - # There isn't enough data in the stream. Probably the socket terminated - break - - -def _read_payload(socket, payload_size): - """ - From the given socket, reads and yields payload of the given size. With sockets, we don't receive all data at - once. Therefore this method will yield each time we read some data from the socket until the payload_size has - reached or socket has no more data. - - Parameters - ---------- - socket - Socket to read from - - payload_size : int - Size of the payload to read. Exactly these many bytes are read from the socket before stopping the yield. - - Yields - ------- - int - Type of the stream (1 => stdout, 2 => stderr) - str - Data in the stream - """ - - remaining = payload_size - while remaining > 0: - - # Try and read as much as possible - data = read(socket, remaining) - if data is None: - # ``read`` will terminate with an empty string. This is just a transient state where we didn't get any data - continue - - if len(data) == 0: # pylint: disable=C1801 - # Empty string. Socket does not have any more data. We are done here even if we haven't read full payload - break - - remaining -= len(data) - yield data - - -def _read_header(socket): - """ - Reads the header from socket stream to determine the size of next frame to read. Header is 8 bytes long, where - the first byte is the stream type and last four bytes (bigendian) is size of the payload - - header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} - - Parameters - ---------- - socket - Socket to read the responses from - - Returns - ------- - int - Type of the frame - int - Size of the payload - """ - - data = read_exactly(socket, 8) - - # >BxxxL is the struct notation to unpack data in correct header format in big-endian - return struct.unpack(">BxxxL", data) diff --git a/samcli/local/docker/container.py b/samcli/local/docker/container.py index a2ffc15e77..42e8d21473 100644 --- a/samcli/local/docker/container.py +++ b/samcli/local/docker/container.py @@ -8,7 +8,6 @@ import docker -from samcli.local.docker.attach_api import attach from .utils import to_posix_path LOG = logging.getLogger(__name__) @@ -200,7 +199,7 @@ def wait_for_logs(self, stdout=None, stderr=None): real_container = self.docker_client.containers.get(self.id) # Fetch both stdout and stderr streams from Docker as a single iterator. - logs_itr = attach(self.docker_client, container=real_container, stdout=True, stderr=True, logs=True) + logs_itr = real_container.attach(stream=True, logs=True, demux=True) self._write_container_output(logs_itr, stdout=stdout, stderr=stderr) @@ -238,25 +237,13 @@ def _write_container_output(output_itr, stdout=None, stderr=None): Stream writer to write stderr data from the Container into """ - # Iterator returns a tuple of (frame_type, data) where the frame type determines which stream we write output - # to - for frame_type, data in output_itr: - - if frame_type == Container._STDOUT_FRAME_TYPE and stdout: - # Frame type 1 is stdout data. - stdout.write(data) - - elif frame_type == Container._STDERR_FRAME_TYPE and stderr: - # Frame type 2 is stderr data. - stderr.write(data) - - else: - # Either an unsupported frame type or stream for this frame type is not configured - LOG.debug( - "Dropping Docker container output because of unconfigured frame type. " "Frame Type: %s. Data: %s", - frame_type, - data, - ) + # Iterator returns a tuple of (stdout, stderr) + for stdout_data, stderr_data in output_itr: + if stdout_data and stdout: + stdout.write(stdout_data) + + if stderr_data and stderr: + stderr.write(stderr_data) @property def network_id(self): diff --git a/samcli/local/docker/lambda_debug_entrypoint.py b/samcli/local/docker/lambda_debug_entrypoint.py index 4424942f6f..9868652aea 100644 --- a/samcli/local/docker/lambda_debug_entrypoint.py +++ b/samcli/local/docker/lambda_debug_entrypoint.py @@ -101,6 +101,23 @@ def get_entry_point(debug_port, debug_args_list, runtime, options): ] ), ], + Runtime.nodejs12x.value: [ + "/var/rapid/init", + "--bootstrap", + "/var/lang/bin/node", + "--bootstrap-args", + json.dumps( + debug_args_list + + [ + "--inspect-brk=0.0.0.0:" + str(debug_port), + "--nolazy", + "--expose-gc", + "--max-http-header-size", + "81920", + "/var/runtime/index.js", + ] + ), + ], Runtime.python27.value: ["/usr/bin/python2.7"] + debug_args_list + ["/var/runtime/awslambda/bootstrap.py"], Runtime.python36.value: ["/var/lang/bin/python3.6"] + debug_args_list @@ -112,6 +129,13 @@ def get_entry_point(debug_port, debug_args_list, runtime, options): "--bootstrap-args", json.dumps(debug_args_list + ["/var/runtime/bootstrap"]), ], + Runtime.python38.value: [ + "/var/rapid/init", + "--bootstrap", + "/var/lang/bin/python3.8", + "--bootstrap-args", + json.dumps(debug_args_list + ["/var/runtime/bootstrap"]), + ], } try: return entrypoint_mapping[runtime] diff --git a/samcli/local/docker/lambda_image.py b/samcli/local/docker/lambda_image.py index a8d9d418ed..4bbe4a4eb3 100644 --- a/samcli/local/docker/lambda_image.py +++ b/samcli/local/docker/lambda_image.py @@ -22,11 +22,14 @@ class Runtime(Enum): nodejs610 = "nodejs6.10" nodejs810 = "nodejs8.10" nodejs10x = "nodejs10.x" + nodejs12x = "nodejs12.x" python27 = "python2.7" python36 = "python3.6" python37 = "python3.7" + python38 = "python3.8" ruby25 = "ruby2.5" java8 = "java8" + java11 = "java11" go1x = "go1.x" dotnetcore20 = "dotnetcore2.0" dotnetcore21 = "dotnetcore2.1" diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/cookiecutter.json b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/cookiecutter.json index 9d928e8536..55e10cc85f 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/cookiecutter.json +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/cookiecutter.json @@ -1,4 +1,4 @@ { "project_name": "Name of the project", - "runtime": "java8" + "runtime": "java11" } diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/{{cookiecutter.project_name}}/template.yaml b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/{{cookiecutter.project_name}}/template.yaml index 3c8f5a3f19..532046217d 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/{{cookiecutter.project_name}}/template.yaml +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-gradle/{{cookiecutter.project_name}}/template.yaml @@ -16,7 +16,7 @@ Resources: Properties: CodeUri: HelloWorldFunction Handler: helloworld.App::handleRequest - Runtime: java8 + Runtime: {{ cookiecutter.runtime }} MemorySize: 512 Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/cookiecutter.json b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/cookiecutter.json index 9d928e8536..55e10cc85f 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/cookiecutter.json +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/cookiecutter.json @@ -1,4 +1,4 @@ { "project_name": "Name of the project", - "runtime": "java8" + "runtime": "java11" } diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/{{cookiecutter.project_name}}/template.yaml b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/{{cookiecutter.project_name}}/template.yaml index 3c8f5a3f19..532046217d 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/{{cookiecutter.project_name}}/template.yaml +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-java-maven/{{cookiecutter.project_name}}/template.yaml @@ -16,7 +16,7 @@ Resources: Properties: CodeUri: HelloWorldFunction Handler: helloworld.App::handleRequest - Runtime: java8 + Runtime: {{ cookiecutter.runtime }} MemorySize: 512 Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object Variables: diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/cookiecutter.json b/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/cookiecutter.json index 60b6c69147..b0bf4a95e8 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/cookiecutter.json +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/cookiecutter.json @@ -1,4 +1,4 @@ { "project_name": "Name of the project", - "runtime": "nodejs10.x" + "runtime": "nodejs12.x" } \ No newline at end of file diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/{{cookiecutter.project_name}}/template.yaml b/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/{{cookiecutter.project_name}}/template.yaml index 3ffdbf536b..7b519ad861 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/{{cookiecutter.project_name}}/template.yaml +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs/{{cookiecutter.project_name}}/template.yaml @@ -16,11 +16,7 @@ Resources: Properties: CodeUri: hello-world/ Handler: app.lambdaHandler - {%- if cookiecutter.runtime == 'nodejs8.10' %} Runtime: {{ cookiecutter.runtime }} - {%- else %} - Runtime: nodejs10.x - {%- endif %} Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/cookiecutter.json b/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/cookiecutter.json index 3599f3691c..d924273933 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/cookiecutter.json +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/cookiecutter.json @@ -1,4 +1,4 @@ { "project_name": "Name of the project", - "runtime": "python3.7" + "runtime": "python3.8" } \ No newline at end of file diff --git a/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/{{cookiecutter.project_name}}/template.yaml b/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/{{cookiecutter.project_name}}/template.yaml index 48c274e192..d93d7959c6 100644 --- a/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/{{cookiecutter.project_name}}/template.yaml +++ b/samcli/local/init/templates/cookiecutter-aws-sam-hello-python/{{cookiecutter.project_name}}/template.yaml @@ -16,13 +16,7 @@ Resources: Properties: CodeUri: hello_world/ Handler: app.lambda_handler - {%- if cookiecutter.runtime == 'python2.7' %} - Runtime: python2.7 - {%- elif cookiecutter.runtime == 'python3.6' %} - Runtime: python3.6 - {%- else %} - Runtime: python3.7 - {%- endif %} + Runtime: {{ cookiecutter.runtime }} Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api diff --git a/tests/integration/buildcmd/test_build_cmd.py b/tests/integration/buildcmd/test_build_cmd.py index fd96ed5066..4f44590ab7 100644 --- a/tests/integration/buildcmd/test_build_cmd.py +++ b/tests/integration/buildcmd/test_build_cmd.py @@ -26,7 +26,6 @@ class TestBuildCommand_PythonFunctions(BuildIntegBase): "main.py", "numpy", # 'cryptography', - "jinja2", "requirements.txt", } @@ -37,20 +36,14 @@ class TestBuildCommand_PythonFunctions(BuildIntegBase): ("python2.7", False), ("python3.6", False), ("python3.7", False), + ("python3.8", False), ("python2.7", "use_container"), ("python3.6", "use_container"), ("python3.7", "use_container"), + ("python3.8", "use_container"), ] ) def test_with_default_requirements(self, runtime, use_container): - - # Don't run test on wrong Python versions - py_version = self._get_python_version() - if py_version != runtime: - self.skipTest( - "Current Python version '{}' does not match Lambda runtime version '{}'".format(py_version, runtime) - ) - overrides = {"Runtime": runtime, "CodeUri": "Python", "Handler": "main.handler"} cmdlist = self.get_command_list(use_container=use_container, parameter_overrides=overrides) @@ -72,7 +65,7 @@ def test_with_default_requirements(self, runtime, use_container): ), ) - expected = {"pi": "3.14", "jinja": "Hello World"} + expected = {"pi": "3.14"} self._verify_invoke_built_function( self.built_template, self.FUNCTION_LOGICAL_ID, self._make_parameter_override_arg(overrides), expected ) @@ -136,9 +129,11 @@ class TestBuildCommand_NodeFunctions(BuildIntegBase): ("nodejs6.10", False), ("nodejs8.10", False), ("nodejs10.x", False), + ("nodejs12.x", False), ("nodejs6.10", "use_container"), ("nodejs8.10", "use_container"), ("nodejs10.x", "use_container"), + ("nodejs12.x", "use_container"), ] ) def test_with_default_package_json(self, runtime, use_container): @@ -296,6 +291,11 @@ class TestBuildCommand_Java(BuildIntegBase): ("java8", USING_GRADLE_KOTLIN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), ("java8", USING_MAVEN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_MAVEN), ("java8", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_GRADLEW_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_GRADLE_KOTLIN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_MAVEN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_MAVEN), + ("java11", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), ] ) def test_building_java_in_container(self, runtime, code_path, expected_files): @@ -310,7 +310,19 @@ def test_building_java_in_container(self, runtime, code_path, expected_files): ("java8", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), ] ) - def test_building_java_in_process(self, runtime, code_path, expected_files): + def test_building_java8_in_process(self, runtime, code_path, expected_files): + self._test_with_building_java(runtime, code_path, expected_files, False) + + @parameterized.expand( + [ + ("java11", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_GRADLEW_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_GRADLE_KOTLIN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ("java11", USING_MAVEN_PATH, EXPECTED_FILES_PROJECT_MANIFEST_MAVEN), + ("java11", USING_GRADLE_PATH, EXPECTED_FILES_PROJECT_MANIFEST_GRADLE), + ] + ) + def test_building_java11_in_process(self, runtime, code_path, expected_files): self._test_with_building_java(runtime, code_path, expected_files, False) def _test_with_building_java(self, runtime, code_path, expected_files, use_container): @@ -492,7 +504,6 @@ class TestBuildCommand_SingleFunctionBuilds(BuildIntegBase): "main.py", "numpy", # 'cryptography', - "jinja2", "requirements.txt", } @@ -515,13 +526,6 @@ def test_function_not_found(self): ] ) def test_build_single_function(self, runtime, use_container, function_identifier): - # Don't run test on wrong Python versions - py_version = self._get_python_version() - if py_version != runtime: - self.skipTest( - "Current Python version '{}' does not match Lambda runtime version '{}'".format(py_version, runtime) - ) - overrides = {"Runtime": runtime, "CodeUri": "Python", "Handler": "main.handler"} cmdlist = self.get_command_list( use_container=use_container, parameter_overrides=overrides, function_identifier=function_identifier @@ -533,7 +537,7 @@ def test_build_single_function(self, runtime, use_container, function_identifier self._verify_built_artifact(self.default_build_dir, function_identifier, self.EXPECTED_FILES_PROJECT_MANIFEST) - expected = {"pi": "3.14", "jinja": "Hello World"} + expected = {"pi": "3.14"} self._verify_invoke_built_function( self.built_template, function_identifier, self._make_parameter_override_arg(overrides), expected ) diff --git a/tests/integration/init/test_init_command.py b/tests/integration/init/test_init_command.py index 9ba5418bc1..bad04e227f 100644 --- a/tests/integration/init/test_init_command.py +++ b/tests/integration/init/test_init_command.py @@ -116,7 +116,7 @@ def test_init_command_with_extra_context_parameter(self): "--name", "sam-app-maven", "--no-interactive", - "--extra_context", + "--extra-context", '{"schema_name": "codedeploy", "schema_type": "aws"}', "-o", temp, diff --git a/tests/integration/local/start_lambda/test_start_lambda.py b/tests/integration/local/start_lambda/test_start_lambda.py index 47b9cf742a..b57296f1bd 100644 --- a/tests/integration/local/start_lambda/test_start_lambda.py +++ b/tests/integration/local/start_lambda/test_start_lambda.py @@ -1,5 +1,6 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from time import time +import json import pytest @@ -159,13 +160,19 @@ def test_invoke_with_invocation_type_RequestResponse(self): @pytest.mark.timeout(timeout=300, method="thread") def test_lambda_function_raised_error(self): response = self.lambda_client.invoke(FunctionName="RaiseExceptionFunction", InvocationType="RequestResponse") + response_data = json.loads(response.get("Payload").read().decode("utf-8")) + + print(response_data) self.assertEqual( - response.get("Payload").read().decode("utf-8"), - '{"errorMessage": "Lambda is raising an exception", ' - '"errorType": "Exception", ' - '"stackTrace": [["/var/task/main.py", 48, "raise_exception", ' - '"raise Exception(\\"Lambda is raising an exception\\")"]]}', + response_data, + { + "errorMessage": "Lambda is raising an exception", + "errorType": "Exception", + "stackTrace": [ + ' File "/var/task/main.py", line 48, in raise_exception\n raise Exception("Lambda is raising an exception")\n' + ], + }, ) self.assertEqual(response.get("FunctionError"), "Unhandled") self.assertEqual(response.get("StatusCode"), 200) diff --git a/tests/integration/testdata/buildcmd/Python/main.py b/tests/integration/testdata/buildcmd/Python/main.py index fbd0085e12..28f7174621 100644 --- a/tests/integration/testdata/buildcmd/Python/main.py +++ b/tests/integration/testdata/buildcmd/Python/main.py @@ -1,7 +1,6 @@ import numpy # from cryptography.fernet import Fernet -from jinja2 import Template def handler(event, context): @@ -9,6 +8,4 @@ def handler(event, context): # Try using some of the modules to make sure they work & don't crash the process # print(Fernet.generate_key()) - template = Template("Hello {{ name }}") - - return {"pi": "{0:.2f}".format(numpy.pi), "jinja": template.render(name="World")} + return {"pi": "{0:.2f}".format(numpy.pi)} diff --git a/tests/integration/testdata/buildcmd/Python/requirements.txt b/tests/integration/testdata/buildcmd/Python/requirements.txt index 74a5095ee3..bf8549f936 100644 --- a/tests/integration/testdata/buildcmd/Python/requirements.txt +++ b/tests/integration/testdata/buildcmd/Python/requirements.txt @@ -4,4 +4,3 @@ numpy~=1.15 # `cryptography` has a dependency on `pycparser` which, for some reason doesn't build inside a Docker container. # Turning this off until we resolve this issue: https://github.com/awslabs/aws-lambda-builders/issues/29 # cryptography~=2.4 -Jinja2~=2.10 diff --git a/tests/unit/commands/init/test_cli.py b/tests/unit/commands/init/test_cli.py index 72125793d5..3c7a9b1274 100644 --- a/tests/unit/commands/init/test_cli.py +++ b/tests/unit/commands/init/test_cli.py @@ -148,12 +148,12 @@ def test_init_cli_interactive_multiple_dep_mgrs(self, generate_project_patch, sd generate_project_patch.assert_called_once_with( # need to change the location validation check ANY, - "java8", + "java11", "gradle", ".", "test-project", True, - {"project_name": "test-project", "runtime": "java8"}, + {"project_name": "test-project", "runtime": "java11"}, ) @patch("samcli.commands.init.init_templates.InitTemplates._shared_dir_check") diff --git a/tests/unit/local/docker/test_container.py b/tests/unit/local/docker/test_container.py index e38ff04486..0ca5606e08 100644 --- a/tests/unit/local/docker/test_container.py +++ b/tests/unit/local/docker/test_container.py @@ -435,8 +435,7 @@ def setUp(self): self.container.is_created = Mock() - @patch("samcli.local.docker.container.attach") - def test_must_fetch_stdout_and_stderr_data(self, attach_mock): + def test_must_fetch_stdout_and_stderr_data(self): self.container.is_created.return_value = True @@ -444,7 +443,7 @@ def test_must_fetch_stdout_and_stderr_data(self, attach_mock): self.mock_docker_client.containers.get.return_value = real_container_mock output_itr = Mock() - attach_mock.return_value = output_itr + real_container_mock.attach.return_value = output_itr self.container._write_container_output = Mock() stdout_mock = Mock() @@ -452,9 +451,7 @@ def test_must_fetch_stdout_and_stderr_data(self, attach_mock): self.container.wait_for_logs(stdout=stdout_mock, stderr=stderr_mock) - attach_mock.assert_called_with( - self.mock_docker_client, container=real_container_mock, stdout=True, stderr=True, logs=True - ) + real_container_mock.attach.assert_called_with(stream=True, logs=True, demux=True) self.container._write_container_output.assert_called_with(output_itr, stdout=stdout_mock, stderr=stderr_mock) def test_must_skip_if_no_stdout_and_stderr(self): @@ -472,17 +469,7 @@ def test_must_raise_if_container_is_not_created(self): class TestContainer_write_container_output(TestCase): def setUp(self): - self.output_itr = [ - (Container._STDOUT_FRAME_TYPE, b"stdout1"), - (Container._STDERR_FRAME_TYPE, b"stderr1"), - (30, b"invalid1"), - (Container._STDOUT_FRAME_TYPE, b"stdout2"), - (Container._STDERR_FRAME_TYPE, b"stderr2"), - (30, b"invalid2"), - (Container._STDOUT_FRAME_TYPE, b"stdout3"), - (Container._STDERR_FRAME_TYPE, b"stderr3"), - (30, b"invalid3"), - ] + self.output_itr = [(b"stdout1", None), (None, b"stderr1"), (b"stdout2", b"stderr2"), (None, None)] self.stdout_mock = Mock() self.stderr_mock = Mock() @@ -492,15 +479,15 @@ def test_must_write_stdout_and_stderr_data(self): Container._write_container_output(self.output_itr, stdout=self.stdout_mock, stderr=self.stderr_mock) - self.stdout_mock.write.assert_has_calls([call(b"stdout1"), call(b"stdout2"), call(b"stdout3")]) + self.stdout_mock.write.assert_has_calls([call(b"stdout1"), call(b"stdout2")]) - self.stderr_mock.write.assert_has_calls([call(b"stderr1"), call(b"stderr2"), call(b"stderr3")]) + self.stderr_mock.write.assert_has_calls([call(b"stderr1"), call(b"stderr2")]) def test_must_write_only_stdout(self): Container._write_container_output(self.output_itr, stdout=self.stdout_mock, stderr=None) - self.stdout_mock.write.assert_has_calls([call(b"stdout1"), call(b"stdout2"), call(b"stdout3")]) + self.stdout_mock.write.assert_has_calls([call(b"stdout1"), call(b"stdout2")]) self.stderr_mock.write.assert_not_called() # stderr must never be called @@ -511,7 +498,7 @@ def test_must_write_only_stderr(self): self.stdout_mock.write.assert_not_called() - self.stderr_mock.write.assert_has_calls([call(b"stderr1"), call(b"stderr2"), call(b"stderr3")]) + self.stderr_mock.write.assert_has_calls([call(b"stderr1"), call(b"stderr2")]) class TestContainer_image(TestCase):