From 7cb1e177e6748e7f7fc516a92646604d00e13bd1 Mon Sep 17 00:00:00 2001 From: monkut Date: Thu, 24 Nov 2022 21:17:40 +0900 Subject: [PATCH] Bypass python version runtime check when code run in docker (#1180) * :sparkles: Handle spaces in x-forwared-for (#1127) * :fire: remove `six` (no longer needed as 2.x is no longer supported) * :white_check_mark: add testcase for SUPPORTED_VERSION check to increase code coverage. * :art: run black/isort * :wrench: rename function for clarity * :fire: remove unnecessary import * :wrench: change name of unused mock instance * :sparkles: (#879) Fix url decoding for query string * :wrench: change docstring type multi-line comment to standard multiline comment :sparkles: handle case where "requestContext" is not provided by the event. > Systems calling the Lambda (other than API Gateway) may not provide the field requestContext in the event. :white_check_mark: add testcases * :fire: remove duplicate comment * :pencil: add docstring to util function, `get_unsupported_sys_versioninfo()` * :sparkles: Add any python version support in the case where code is run in docker * :art: run black/isort * :white_check_mark: fix testcase :white_check_mark: update testcase to output the full filepath of file you are expected to compare the README.md to. * :wrench: Add python minor version check when using docker. * :art: fix flake8 * :wrench: don't limit the upper version of zappa on setup. Internal checks will perform version check, but docker based installs shouldn't be capped. --- README.md | 15 ++++++++++++++- setup.py | 2 +- tests/tests.py | 27 +++++++++++++++++++++++++-- tests/tests_docs.py | 5 +++-- tests/utils.py | 6 +++--- zappa/__init__.py | 32 ++++++++++++++++++++++++++++---- 6 files changed, 74 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 49ecb2b79..1d5aaf13a 100644 --- a/README.md +++ b/README.md @@ -222,7 +222,20 @@ This creates a new archive, uploads it to S3 and updates the Lambda function to #### Docker Workflows -In [version 0.53.0](https://github.com/zappa/Zappa/blob/master/CHANGELOG.md), support was added to deploy & update Lambda functions using Docker. Refer to [the blog post](https://ianwhitestone.work/zappa-serverless-docker/) for more details about how to leverage this functionality, and when you may want to. +In [version 0.53.0](https://github.com/zappa/Zappa/blob/master/CHANGELOG.md), support was added to deploy & update Lambda functions using Docker. + +You can specify an ECR image using the `--docker-image-uri` option to the zappa command on `deploy` and `update`. +Zappa expects that the image is built and pushed to a Amazon ECR repository. + +Deploy Example: + + $ zappa deploy --docker-image-uri {AWS ACCOUNT ID}.dkr.ecr.{REGION}.amazonaws.com/{REPOSITORY NAME}:latest + +Update Example: + + $ zappa update --docker-image-uri {AWS ACCOUNT ID}.dkr.ecr.{REGION}.amazonaws.com/{REPOSITORY NAME}:latest + +Refer to [the blog post](https://ianwhitestone.work/zappa-serverless-docker/) for more details about how to leverage this functionality, and when you may want to. ### Rollback diff --git a/setup.py b/setup.py index af89dea10..88fd418c0 100755 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ version=__version__, packages=["zappa"], install_requires=required, - python_requires=">=3.7, <3.10", + python_requires=">=3.7", tests_require=test_required, test_suite="nose.collector", include_package_data=True, diff --git a/tests/tests.py b/tests/tests.py index 563e158f5..0fa347048 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -13,6 +13,7 @@ import unittest import uuid import zipfile +from functools import partial from io import BytesIO from subprocess import check_output @@ -40,7 +41,7 @@ ) from zappa.wsgi import common_log, create_wsgi_request -from .utils import get_unsupported_sys_versioninfo +from .utils import get_sys_versioninfo def random_string(length): @@ -2553,7 +2554,7 @@ def test_delete_lambda_concurrency(self, client): FunctionName="abc", ) - @mock.patch("sys.version_info", new_callable=get_unsupported_sys_versioninfo) + @mock.patch("sys.version_info", new_callable=get_sys_versioninfo) def test_unsupported_version_error(self, *_): from importlib import reload @@ -2562,6 +2563,28 @@ def test_unsupported_version_error(self, *_): reload(zappa) + @mock.patch("pathlib.Path.read_text", return_value="/docker/") + @mock.patch("sys.version_info", new_callable=partial(get_sys_versioninfo, 6)) + def test_minor_version_only_check_when_in_docker(self, *_): + from importlib import reload + + with self.assertRaises(RuntimeError): + import zappa + + reload(zappa) + + @mock.patch("pathlib.Path.read_text", return_value="/docker/") + @mock.patch("sys.version_info", new_callable=partial(get_sys_versioninfo, 7)) + def test_no_runtimeerror_when_in_docker(self, *_): + from importlib import reload + + try: + import zappa + + reload(zappa) + except RuntimeError: + self.fail() + def test_wsgi_query_string_unquoted(self): event = { "body": {}, diff --git a/tests/tests_docs.py b/tests/tests_docs.py index 7c45156ad..0473a465b 100644 --- a/tests/tests_docs.py +++ b/tests/tests_docs.py @@ -80,10 +80,11 @@ def test_readmetoc(self): ) if environ.get("ZAPPA_TEST_SAVE_README_NEW"): - with open(path.join(path.dirname(DIR), "README.test.md"), "w") as f: + readme_test_output_filepath = path.join(path.dirname(DIR), "README.test.md") + with open(readme_test_output_filepath, "w") as f: f.write(new_readme) - msg = "README.test.md written so you can manually compare." + msg = f"{readme_test_output_filepath} written so you can manually compare." else: msg = "You can set environ[ZAPPA_TEST_SAVE_README_NEW]=1 to generate\n" " README.test.md to manually compare." diff --git a/tests/utils.py b/tests/utils.py index 725947c84..fc2a09d71 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -80,7 +80,7 @@ def is_base64(test_string: str) -> bool: return False -def get_unsupported_sys_versioninfo() -> tuple: - """Mock used to test the python unsupported version testcase""" +def get_sys_versioninfo(minor_version: int = 6) -> tuple: + """Mock used to test the python version check testcases""" invalid_versioninfo = namedtuple("version_info", ["major", "minor", "micro", "releaselevel", "serial"]) - return invalid_versioninfo(3, 6, 1, "final", 0) + return invalid_versioninfo(3, minor_version, 1, "final", 0) diff --git a/zappa/__init__.py b/zappa/__init__.py index 0513a377f..6aa5f3f8b 100644 --- a/zappa/__init__.py +++ b/zappa/__init__.py @@ -1,12 +1,36 @@ import sys +from pathlib import Path + + +def running_in_docker() -> bool: + """ + Determine if zappa is running in docker. + - When docker is used allow usage of any python version + """ + # https://stackoverflow.com/a/20012536/24718 + cgroup_content = Path("/proc/1/cgroup").read_text() + in_docker = "/docker/" in cgroup_content or "/lxc/" in cgroup_content + return in_docker + SUPPORTED_VERSIONS = [(3, 7), (3, 8), (3, 9)] +MINIMUM_SUPPORTED_MINOR_VERSION = 7 -if sys.version_info[:2] not in SUPPORTED_VERSIONS: +if not running_in_docker() and sys.version_info[:2] not in SUPPORTED_VERSIONS: + print(running_in_docker()) formatted_supported_versions = ["{}.{}".format(*version) for version in SUPPORTED_VERSIONS] - err_msg = "This version of Python ({}.{}) is not supported!\n".format( - *sys.version_info - ) + "Zappa (and AWS Lambda) support the following versions of Python: {}".format(formatted_supported_versions) + err_msg = ( + f"This version of Python ({sys.version_info.major}.{sys.version_info.minor}) is not supported!\n" + f"Zappa (and AWS Lambda) support the following versions of Python: {formatted_supported_versions}" + ) raise RuntimeError(err_msg) +elif running_in_docker() and sys.version_info.minor < MINIMUM_SUPPORTED_MINOR_VERSION: + # when running in docker enforce minimum version only + err_msg = ( + f"This version of Python ({sys.version_info.major}.{sys.version_info.minor}) is not supported!\n" + f"Zappa requires a minimum version of 3.{MINIMUM_SUPPORTED_MINOR_VERSION}" + ) + raise RuntimeError(err_msg) + __version__ = "0.55.0"