From 30a44242a65d5ede69b984292d1eb5d3f711725a Mon Sep 17 00:00:00 2001 From: ian-whitestone Date: Sun, 13 Jun 2021 15:52:12 -0400 Subject: [PATCH 1/3] Reformat files with black --- tests/test_handler.py | 30 +++------- tests/tests.py | 112 +++++++++----------------------------- tests/tests_middleware.py | 4 +- tests/tests_placebo.py | 15 ++--- zappa/cli.py | 12 +--- zappa/core.py | 35 +++--------- zappa/letsencrypt.py | 23 ++------ 7 files changed, 54 insertions(+), 177 deletions(-) diff --git a/tests/test_handler.py b/tests/test_handler.py index 1089ef798..ad44aff34 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -82,9 +82,7 @@ def test_wsgi_script_name_on_aws_url(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": { - "Host": "1234567890.execute-api.us-east-1.amazonaws.com", - }, + "headers": {"Host": "1234567890.execute-api.us-east-1.amazonaws.com",}, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -110,9 +108,7 @@ def test_wsgi_script_name_on_domain_url(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": { - "Host": "example.com", - }, + "headers": {"Host": "example.com",}, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -135,9 +131,7 @@ def test_wsgi_script_name_with_multi_value_header(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "multiValueHeaders": { - "Host": ["example.com"], - }, + "multiValueHeaders": {"Host": ["example.com"],}, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -158,9 +152,7 @@ def test_wsgi_script_name_with_multi_value_querystring(self): "resource": "/{proxy+}", "requestContext": {}, "multiValueQueryStringParameters": {"multi": ["value", "qs"]}, - "multiValueHeaders": { - "Host": ["example.com"], - }, + "multiValueHeaders": {"Host": ["example.com"],}, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -208,9 +200,7 @@ def test_exception_handler_on_web_request(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": { - "Host": "1234567890.execute-api.us-east-1.amazonaws.com", - }, + "headers": {"Host": "1234567890.execute-api.us-east-1.amazonaws.com",}, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -264,10 +254,7 @@ def test_bot_triggered_event(self): "messageVersion": "1.0", "invocationSource": "DialogCodeHook", "userId": "user-id specified in the POST request to Amazon Lex.", - "sessionAttributes": { - "key1": "value1", - "key2": "value2", - }, + "sessionAttributes": {"key1": "value1", "key2": "value2",}, "bot": {"name": "bot-name", "alias": "bot-alias", "version": "bot-version"}, "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request", "currentIntent": { @@ -295,10 +282,7 @@ def test_exception_in_bot_triggered_event(self): "messageVersion": "1.0", "invocationSource": "DialogCodeHook", "userId": "user-id specified in the POST request to Amazon Lex.", - "sessionAttributes": { - "key1": "value1", - "key2": "value2", - }, + "sessionAttributes": {"key1": "value1", "key2": "value2",}, "bot": {"name": "bot-name", "alias": "bot-alias", "version": "bot-version"}, "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request", "currentIntent": { diff --git a/tests/tests.py b/tests/tests.py index f08b256e8..dff19705b 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -270,10 +270,7 @@ def test_getting_installed_packages_mixed_case_location(self, *args): z.get_installed_packages( "/venv/Site-packages", "/venv/site-packages64" ), - { - "superpackage": "0.1", - "superpackage64": "0.1", - }, + {"superpackage": "0.1", "superpackage64": "0.1",}, ) def test_getting_installed_packages_mixed_case(self, *args): @@ -1773,9 +1770,7 @@ def get_domain_name(domain, *_args, **_kwargs): # Test creating domain without Route53 zappa_cli.zappa_settings["stage"].update( - { - "route53_enabled": False, - } + {"route53_enabled": False,} ) zappa_cli.zappa.reset_mock() zappa_cli.zappa.domain_names["test.example.com"] = "" @@ -1869,10 +1864,7 @@ def test_get_all_zones_two_pages(self, client): zones = zappa_core.get_all_zones() zappa_core.route53.list_hosted_zones.assert_has_calls( - [ - mock.call(MaxItems="100"), - mock.call(MaxItems="100", Marker="101"), - ] + [mock.call(MaxItems="100"), mock.call(MaxItems="100", Marker="101"),] ) self.assertListEqual(zones["HostedZones"], [{"Id": "zone1"}, {"Id": "zone2"}]) @@ -2367,9 +2359,7 @@ def test_zappa_core_deploy_lambda_alb(self): ) elbv2_stubber.add_response( "describe_load_balancers", - expected_params={ - "LoadBalancerArns": [loadbalancer_arn], - }, + expected_params={"LoadBalancerArns": [loadbalancer_arn],}, service_response={ "LoadBalancers": [ {"LoadBalancerArn": loadbalancer_arn, "State": {"Code": "active"}} @@ -2393,17 +2383,8 @@ def test_zappa_core_deploy_lambda_alb(self): elbv2_stubber.add_response( "create_target_group", - expected_params={ - "Name": kwargs["lambda_name"], - "TargetType": "lambda", - }, - service_response={ - "TargetGroups": [ - { - "TargetGroupArn": targetgroup_arn, - } - ] - }, + expected_params={"Name": kwargs["lambda_name"], "TargetType": "lambda",}, + service_response={"TargetGroups": [{"TargetGroupArn": targetgroup_arn,}]}, ) elbv2_stubber.add_response( "modify_target_group_attributes", @@ -2445,15 +2426,10 @@ def test_zappa_core_deploy_lambda_alb(self): "create_listener", expected_params={ "Certificates": [ - { - "CertificateArn": kwargs["alb_vpc_config"]["CertificateArn"], - } + {"CertificateArn": kwargs["alb_vpc_config"]["CertificateArn"],} ], "DefaultActions": [ - { - "Type": "forward", - "TargetGroupArn": targetgroup_arn, - } + {"Type": "forward", "TargetGroupArn": targetgroup_arn,} ], "LoadBalancerArn": loadbalancer_arn, "Protocol": "HTTPS", @@ -2498,77 +2474,48 @@ def test_zappa_core_undeploy_lambda_alb(self): ) elbv2_stubber.add_response( "describe_load_balancers", - expected_params={ - "Names": [kwargs["lambda_name"]], - }, + expected_params={"Names": [kwargs["lambda_name"]],}, service_response={ - "LoadBalancers": [ - { - "LoadBalancerArn": loadbalancer_arn, - } - ] + "LoadBalancers": [{"LoadBalancerArn": loadbalancer_arn,}] }, ) elbv2_stubber.add_response( "describe_listeners", - expected_params={ - "LoadBalancerArn": loadbalancer_arn, - }, - service_response={ - "Listeners": [ - { - "ListenerArn": listener_arn, - } - ] - }, + expected_params={"LoadBalancerArn": loadbalancer_arn,}, + service_response={"Listeners": [{"ListenerArn": listener_arn,}]}, ) elbv2_stubber.add_response( "delete_listener", - expected_params={ - "ListenerArn": listener_arn, - }, + expected_params={"ListenerArn": listener_arn,}, service_response={}, ) elbv2_stubber.add_response( "delete_load_balancer", - expected_params={ - "LoadBalancerArn": loadbalancer_arn, - }, + expected_params={"LoadBalancerArn": loadbalancer_arn,}, service_response={}, ) elbv2_stubber.add_client_error( - "describe_load_balancers", - service_error_code="LoadBalancerNotFound", + "describe_load_balancers", service_error_code="LoadBalancerNotFound", ) lambda_stubber.add_response( "get_function", - expected_params={ - "FunctionName": kwargs["lambda_name"], - }, + expected_params={"FunctionName": kwargs["lambda_name"],}, service_response={"Configuration": {"FunctionArn": function_arn}}, ) elbv2_stubber.add_response( "describe_target_groups", - expected_params={ - "Names": [kwargs["lambda_name"]], - }, - service_response={ - "TargetGroups": [{"TargetGroupArn": targetgroup_arn}], - }, + expected_params={"Names": [kwargs["lambda_name"]],}, + service_response={"TargetGroups": [{"TargetGroupArn": targetgroup_arn}],}, ) elbv2_stubber.add_response( - "deregister_targets", - service_response={}, + "deregister_targets", service_response={}, ) elbv2_stubber.add_client_error( - "describe_target_health", - service_error_code="InvalidTarget", + "describe_target_health", service_error_code="InvalidTarget", ) elbv2_stubber.add_response( "delete_target_group", - expected_params={ - "TargetGroupArn": targetgroup_arn, - }, + expected_params={"TargetGroupArn": targetgroup_arn,}, service_response={}, ) lambda_stubber.activate() @@ -2588,12 +2535,9 @@ def test_set_lambda_concurrency(self, client): "FunctionArn": "abc", "Version": 1, } - access_logging_patch = zappa_core.create_lambda_function( - concurrency=5, - ) + access_logging_patch = zappa_core.create_lambda_function(concurrency=5,) boto_mock.client().put_function_concurrency.assert_called_with( - FunctionName="abc", - ReservedConcurrentExecutions=5, + FunctionName="abc", ReservedConcurrentExecutions=5, ) @mock.patch("botocore.client") @@ -2610,13 +2554,10 @@ def test_update_lambda_concurrency(self, client): "Version": 1, } access_logging_patch = zappa_core.update_lambda_function( - bucket="test", - function_name="abc", - concurrency=5, + bucket="test", function_name="abc", concurrency=5, ) boto_mock.client().put_function_concurrency.assert_called_with( - FunctionName="abc", - ReservedConcurrentExecutions=5, + FunctionName="abc", ReservedConcurrentExecutions=5, ) boto_mock.client().delete_function_concurrency.assert_not_called() @@ -2634,8 +2575,7 @@ def test_delete_lambda_concurrency(self, client): "Version": 1, } access_logging_patch = zappa_core.update_lambda_function( - bucket="test", - function_name="abc", + bucket="test", function_name="abc", ) boto_mock.client().put_function_concurrency.assert_not_called() boto_mock.client().delete_function_concurrency.assert_called_with( diff --git a/tests/tests_middleware.py b/tests/tests_middleware.py index 81d82128d..40aff46e7 100644 --- a/tests/tests_middleware.py +++ b/tests/tests_middleware.py @@ -161,9 +161,7 @@ def test_wsgi_map_context_headers_handling(self): "body": {}, "headers": {"Content-Type": "application/json"}, "pathParameters": {"proxy": "v1/runs"}, - "requestContext": { - "authorizer": {"principalId": "user1"}, - }, + "requestContext": {"authorizer": {"principalId": "user1"},}, "query": {}, } diff --git a/tests/tests_placebo.py b/tests/tests_placebo.py index 5bffd04a4..c203509b0 100644 --- a/tests/tests_placebo.py +++ b/tests/tests_placebo.py @@ -49,8 +49,7 @@ def test_upload_remove_s3(self, session): # will throw ClientError with 404 if object doesn't exist s3.meta.client.head_object( - Bucket=bucket_name, - Key=zip_path, + Bucket=bucket_name, Key=zip_path, ) res = z.remove_from_s3(zip_path, bucket_name) self.assertTrue(res) @@ -78,8 +77,7 @@ def test_copy_on_s3(self, session): # will throw ClientError with 404 if object doesn't exist s3.meta.client.head_object( - Bucket=bucket_name, - Key=zip_path, + Bucket=bucket_name, Key=zip_path, ) zp = "copy_" + zip_path res = z.copy_on_s3(zip_path, zp, bucket_name) @@ -104,9 +102,7 @@ def test_create_lambda_function_s3(self, session): ) arn = z.update_lambda_function( - bucket=bucket_name, - s3_key=zip_path, - function_name="test_lmbda_function55", + bucket=bucket_name, s3_key=zip_path, function_name="test_lmbda_function55", ) @placebo_session @@ -188,10 +184,7 @@ def test_handler(self, session): event = { "body": {}, "headers": {}, - "params": { - "parameter_1": "asdf1", - "parameter_2": "asdf2", - }, + "params": {"parameter_1": "asdf1", "parameter_2": "asdf2",}, "method": "GET", "query": {}, } diff --git a/zappa/cli.py b/zappa/cli.py index c25eb6c09..9eb6a00a8 100755 --- a/zappa/cli.py +++ b/zappa/cli.py @@ -649,9 +649,7 @@ def dispatch_command(self, command, stage): command = command_tail[0] # ex: zappa manage dev showmigrations admin self.invoke( - command, - command="manage", - no_color=self.vargs["no_color"], + command, command="manage", no_color=self.vargs["no_color"], ) elif command == "tail": # pragma: no cover @@ -1450,9 +1448,7 @@ def unschedule(self): print("Unscheduling..") self.zappa.unschedule_events( - lambda_name=self.lambda_name, - lambda_arn=function_arn, - events=events, + lambda_name=self.lambda_name, lambda_arn=function_arn, events=events, ) # Remove async task SNS @@ -1481,9 +1477,7 @@ def invoke(self, function_name, raw_python=False, command=None, no_color=False): import json as json response = self.zappa.invoke_lambda_function( - self.lambda_name, - json.dumps(command), - invocation_type="RequestResponse", + self.lambda_name, json.dumps(command), invocation_type="RequestResponse", ) if "LogResult" in response: diff --git a/zappa/core.py b/zappa/core.py index a3da0c608..3b4a76445 100644 --- a/zappa/core.py +++ b/zappa/core.py @@ -1059,9 +1059,7 @@ def upload_to_s3(self, source_path, bucket_name, disable_progress=False): # it's actually "US Standard", or something. # More here: https://github.com/boto/boto3/issues/125 if self.aws_region == "us-east-1": - self.s3_client.create_bucket( - Bucket=bucket_name, - ) + self.s3_client.create_bucket(Bucket=bucket_name,) else: self.s3_client.create_bucket( Bucket=bucket_name, @@ -1243,8 +1241,7 @@ def create_lambda_function( if concurrency is not None: self.lambda_client.put_function_concurrency( - FunctionName=resource_arn, - ReservedConcurrentExecutions=concurrency, + FunctionName=resource_arn, ReservedConcurrentExecutions=concurrency, ) return resource_arn @@ -1284,8 +1281,7 @@ def update_lambda_function( # https://github.com/Miserlou/Zappa/issues/1823 try: response = self.lambda_client.get_alias( - FunctionName=function_name, - Name=ALB_LAMBDA_ALIAS, + FunctionName=function_name, Name=ALB_LAMBDA_ALIAS, ) alias_exists = True except botocore.exceptions.ClientError as e: # pragma: no cover @@ -1302,8 +1298,7 @@ def update_lambda_function( if concurrency is not None: self.lambda_client.put_function_concurrency( - FunctionName=function_name, - ReservedConcurrentExecutions=concurrency, + FunctionName=function_name, ReservedConcurrentExecutions=concurrency, ) else: self.lambda_client.delete_function_concurrency(FunctionName=function_name) @@ -1495,9 +1490,7 @@ def delete_lambda_function(self, function_name): """ print("Deleting Lambda function..") - return self.lambda_client.delete_function( - FunctionName=function_name, - ) + return self.lambda_client.delete_function(FunctionName=function_name,) ## # Application load balancer @@ -1619,12 +1612,7 @@ def deploy_lambda_alb(self, lambda_arn, lambda_name, alb_vpc_config, timeout): kwargs = dict( # TODO: Listeners support custom ssl certificates (Certificates). For now we leave this default. Certificates=[{"CertificateArn": alb_vpc_config["CertificateArn"]}], - DefaultActions=[ - { - "Type": "forward", - "TargetGroupArn": target_group_arn, - } - ], + DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn,}], LoadBalancerArn=load_balancer_arn, Protocol="HTTPS", # TODO: Add option for custom ports @@ -2121,12 +2109,7 @@ def remove_api_compression(self, api_id): """ self.apigateway_client.update_rest_api( restApiId=api_id, - patchOperations=[ - { - "op": "replace", - "path": "/minimumCompressionSize", - } - ], + patchOperations=[{"op": "replace", "path": "/minimumCompressionSize",}], ) def get_api_keys(self, api_id, stage_name): @@ -3326,9 +3309,7 @@ def create_async_dynamodb_table(self, table_name, read_capacity, write_capacity) dynamodb_table = self.dynamodb_client.create_table( AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], TableName=table_name, - KeySchema=[ - {"AttributeName": "id", "KeyType": "HASH"}, - ], + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"},], ProvisionedThroughput={ "ReadCapacityUnits": read_capacity, "WriteCapacityUnits": write_capacity, diff --git a/zappa/letsencrypt.py b/zappa/letsencrypt.py index 9ecd63f02..da22b2d57 100755 --- a/zappa/letsencrypt.py +++ b/zappa/letsencrypt.py @@ -41,11 +41,7 @@ def get_cert_and_update_domain( - zappa_instance, - lambda_name, - api_stage, - domain=None, - manual=False, + zappa_instance, lambda_name, api_stage, domain=None, manual=False, ): """ Main cert installer path. @@ -264,10 +260,7 @@ def get_cert(zappa_instance, log=LOGGER, CA=DEFAULT_CA): # get new challenge code, result = _send_signed_request( CA + "/acme/new-authz", - { - "resource": "new-authz", - "identifier": {"type": "dns", "value": domain}, - }, + {"resource": "new-authz", "identifier": {"type": "dns", "value": domain},}, ) if code != 201: raise ValueError( @@ -364,11 +357,7 @@ def sign_certificate(): devnull = open(os.devnull, "wb") csr_der = subprocess.check_output(cmd, stderr=devnull) code, result = _send_signed_request( - DEFAULT_CA + "/acme/new-cert", - { - "resource": "new-cert", - "csr": _b64(csr_der), - }, + DEFAULT_CA + "/acme/new-cert", {"resource": "new-cert", "csr": _b64(csr_der),}, ) if code != 201: raise ValueError("Error signing certificate: {0} {1}".format(code, result)) @@ -381,10 +370,8 @@ def encode_certificate(result): """ Encode cert bytes to PEM encoded cert file. """ - cert_body = ( - """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( - "\n".join(textwrap.wrap(base64.b64encode(result).decode("utf8"), 64)) - ) + cert_body = """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( + "\n".join(textwrap.wrap(base64.b64encode(result).decode("utf8"), 64)) ) signed_crt = open("{}/signed.crt".format(gettempdir()), "w") signed_crt.write(cert_body) From 5b695e7504e3b9d64177510c9f01b6cd9b0af860 Mon Sep 17 00:00:00 2001 From: ian-whitestone Date: Sun, 18 Apr 2021 11:59:08 -0400 Subject: [PATCH 2/3] Update/deploy a lambda using a Docker container image Part of #922 --- .../lambda.GetFunctionConfiguration_1.json | 3 +- .../lambda.ListVersionsByFunction_2.json | 9 +- .../lambda.ListVersionsByFunction_4.json | 9 +- .../lambda.CreateAlias_1.json | 9 + .../lambda.CreateFunction_1.json | 20 + .../lambda.DeleteFunctionConcurrency_1.json | 9 + .../lambda.GetAlias_1.json | 9 + .../lambda.UpdateAlias_1.json | 9 + .../lambda.UpdateFunctionCode_1.json | 20 + .../lambda.GetFunction_1.json | 29 ++ .../lambda.ListVersionsByFunction_1.json | 27 +- .../lambda.ListVersionsByFunction_2.json | 27 +- .../lambda.ListVersionsByFunction_1.json | 128 +++++ .../lambda.ListVersionsByFunction_2.json | 128 +++++ tests/tests.py | 27 +- tests/tests_placebo.py | 39 ++ zappa/cli.py | 480 ++++++++++-------- zappa/core.py | 130 +++-- 18 files changed, 805 insertions(+), 307 deletions(-) create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateAlias_1.json create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateFunction_1.json create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.DeleteFunctionConcurrency_1.json create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.GetAlias_1.json create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateAlias_1.json create mode 100644 tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateFunctionCode_1.json create mode 100644 tests/placebo/TestZappa.test_is_lambda_function_ready/lambda.GetFunction_1.json create mode 100644 tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_1.json create mode 100644 tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_2.json diff --git a/tests/placebo/TestZappa.test_cli_aws/lambda.GetFunctionConfiguration_1.json b/tests/placebo/TestZappa.test_cli_aws/lambda.GetFunctionConfiguration_1.json index bac7a999f..1d826bbf1 100644 --- a/tests/placebo/TestZappa.test_cli_aws/lambda.GetFunctionConfiguration_1.json +++ b/tests/placebo/TestZappa.test_cli_aws/lambda.GetFunctionConfiguration_1.json @@ -27,6 +27,7 @@ "Version": "$LATEST", "Environment": { "Variables": {} - } + }, + "PackageType": "Zip" } } \ No newline at end of file diff --git a/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_2.json b/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_2.json index f939d9acc..463721dd5 100644 --- a/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_2.json +++ b/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_2.json @@ -18,7 +18,8 @@ "Timeout": 30, "LastModified": "2016-06-02T19:24:32.878+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "1", @@ -32,7 +33,8 @@ "Timeout": 30, "LastModified": "2016-06-02T19:23:48.902+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "2", @@ -46,7 +48,8 @@ "Timeout": 30, "LastModified": "2016-06-02T19:24:32.878+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" } ] } diff --git a/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_4.json b/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_4.json index f0241afd6..e8b68dc6a 100644 --- a/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_4.json +++ b/tests/placebo/TestZappa.test_cli_aws/lambda.ListVersionsByFunction_4.json @@ -25,7 +25,8 @@ "Timeout": 30, "LastModified": "2016-08-25T03:31:23.343+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "4", @@ -39,7 +40,8 @@ "Timeout": 30, "LastModified": "2016-08-25T03:29:40.612+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "5", @@ -53,7 +55,8 @@ "Timeout": 30, "LastModified": "2016-08-25T03:31:18.572+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" } ] } diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateAlias_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateAlias_1.json new file mode 100644 index 000000000..08d0bb9ed --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateAlias_1.json @@ -0,0 +1,9 @@ +{ + "status_code": 201, + "data": { + "AliasArn": "arn:aws:lambda:us-east-1:12345:function:test_lmbda_function55:current-alb-version", + "Description": "Zappa Deployment", + "FunctionVersion": "1", + "Name": "current-alb-version" + } +} diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateFunction_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateFunction_1.json new file mode 100644 index 000000000..61a6508a0 --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.CreateFunction_1.json @@ -0,0 +1,20 @@ +{ + "status_code": 201, + "data": { + "CodeSha256": "q4duEOI611sqtkU+YbdNkjH5qGRlgmvc9+FhpdykYuk=", + "FunctionName": "test_lmbda_function55", + "ResponseMetadata": { + "HTTPStatusCode": 201, + "RequestId": "12b75ef1-e226-11e5-84a2-ad7ddc64ad40" + }, + "CodeSize": 26585626, + "MemorySize": 512, + "FunctionArn": "arn:aws:lambda:us-east-1:12345:function:test_lmbda_function55", + "Version": "1", + "Role": "arn:aws:iam::12345:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-04T16:28:06.633+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + } +} diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.DeleteFunctionConcurrency_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.DeleteFunctionConcurrency_1.json new file mode 100644 index 000000000..c226aef56 --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.DeleteFunctionConcurrency_1.json @@ -0,0 +1,9 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "aff3a3f9-28f4-11e6-9dbb-5dd116b9ddf1" + } + } +} diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.GetAlias_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.GetAlias_1.json new file mode 100644 index 000000000..cf839c230 --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.GetAlias_1.json @@ -0,0 +1,9 @@ +{ + "status_code": 200, + "data": { + "AliasArn": "arn:aws:lambda:us-east-1:12345:function:test_lmbda_function55:current-alb-version", + "Description": "Zappa Deployment", + "FunctionVersion": "1", + "Name": "current-alb-version" + } +} diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateAlias_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateAlias_1.json new file mode 100644 index 000000000..08d0bb9ed --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateAlias_1.json @@ -0,0 +1,9 @@ +{ + "status_code": 201, + "data": { + "AliasArn": "arn:aws:lambda:us-east-1:12345:function:test_lmbda_function55:current-alb-version", + "Description": "Zappa Deployment", + "FunctionVersion": "1", + "Name": "current-alb-version" + } +} diff --git a/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateFunctionCode_1.json b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateFunctionCode_1.json new file mode 100644 index 000000000..fdb149ba8 --- /dev/null +++ b/tests/placebo/TestZappa.test_create_lambda_function_docker/lambda.UpdateFunctionCode_1.json @@ -0,0 +1,20 @@ +{ + "status_code": 200, + "data": { + "CodeSha256": "q4duEOI611sqtkU+YbdNkjH5qGRlgmvc9+FhpdykYuk=", + "FunctionName": "test_lmbda_function55", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "153e09be-e226-11e5-a3b8-7b263f053e5a" + }, + "CodeSize": 26585626, + "MemorySize": 512, + "FunctionArn": "arn:aws:lambda:us-east-1:12345:function:test_lmbda_function55:1", + "Version": "1", + "Role": "arn:aws:iam::12345:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-04T16:28:06.633+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + } +} diff --git a/tests/placebo/TestZappa.test_is_lambda_function_ready/lambda.GetFunction_1.json b/tests/placebo/TestZappa.test_is_lambda_function_ready/lambda.GetFunction_1.json new file mode 100644 index 000000000..7debf4b45 --- /dev/null +++ b/tests/placebo/TestZappa.test_is_lambda_function_ready/lambda.GetFunction_1.json @@ -0,0 +1,29 @@ +{ + "status_code": 200, + "data": { + "Code": { + "RepositoryType": "S3", + "Location": "https://prod-04-2014-tasks.s3.amazonaws.com/snapshots/724336686645/django-helloworld-unicode-a0d56d69-de7c-421b-9527-f49e100a8613?x-amz-security-token=AQoDYXdzEEka4AOil22HD1z9lkxZ7yiced%2FbYEWzXJHbMHexq2zxzMQ7%2Fj2a06AP9z3nK0QgGPUADK2A6FUpFFl%2BjO7gBmP%2FKifl9jTvvaf72YhPbZDJTIrFvZZ%2B5NjhkDrfRgyK%2BhBROGNH85L46iJSShwZ5lmKgADTnnVMT9pZ2JXF3uNLUzrWoJTpf%2F7lDBMEoFO%2BNLer7MsLsuCiemzKV4Kcvo0Mu3qhkk1u2BfoRTj4xmiaWE7UfqD%2FeQkINdwkUgXeQ9SA1T2l70omZ1ss2l1y18xeUvCQde5OrE7ue5cVpENyjYr9dEwPYwm1wG9%2B%2FJE%2BMQZ8Lz6CNex%2BTVuBvY09JL8vTV7WpRxGhr96a7OIn5YCCQ7cJfNVIWpdBUrWmqnE8Jh9oL558TVA0e5RFP9YU%2BFYxJcA6fH%2B0DZBAAK9797XPltzhSXrUcB8JuHfa%2FC1n2w8X7HbsMTI8m1OAIkQQtppYHtl4T5o3012VcSTLRSv945sCodMOmueKsEwAZlc1gFflcwGXE4v4hB6YJjvcRj2SQiK7Yzw7ShODOroOsWT18te8p9Qjs2mL6akLt1%2Fo8tddWskKzqrfYfBTvfFL5Y0nU4sI96JUXM0KXmJotPRmCdFEktGk4Fre0wm%2FyAau0NdaDQg7NzmtgU%3D&AWSAccessKeyId=ASIAIQFLXUK7E7GUTQ2A&Expires=1457113092&Signature=%2FH0RMGKWk1tEGnK9R6CCPXQ6Y04%3D" + }, + "Configuration": { + "Version": "7", + "CodeSha256": "t3pcqjdrLOqd2p0bRsCEOhgvLJ9sLJrVazpGqhDybsc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154784, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:7", + "Handler": "handler.lambda_handler", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:24:01.446+0000", + "Runtime": "python2.7", + "Description": "Zappa Deployment", + "State": "Active", + "LastUpdateStatus": "Successful" + }, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "78da1863-e22e-11e5-a5ae-01cbfdebdc8f" + } + } +} diff --git a/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_1.json b/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_1.json index d88e58f13..9dcb16e68 100644 --- a/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_1.json +++ b/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_1.json @@ -18,7 +18,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:55:35.845+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "1", @@ -32,7 +33,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:39:31.557+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "2", @@ -46,7 +48,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:47:04.499+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "3", @@ -60,7 +63,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:58:08.443+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "4", @@ -74,7 +78,8 @@ "Timeout": 30, "LastModified": "2016-02-29T16:25:49.425+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "5", @@ -88,7 +93,8 @@ "Timeout": 30, "LastModified": "2016-02-29T16:34:47.988+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "6", @@ -102,7 +108,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:12:28.708+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "7", @@ -116,7 +123,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:24:01.446+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "8", @@ -130,7 +138,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:55:35.845+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" } ] } diff --git a/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_2.json b/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_2.json index 0970214cb..5ce591a50 100644 --- a/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_2.json +++ b/tests/placebo/TestZappa.test_rollback_lambda_function_version/lambda.ListVersionsByFunction_2.json @@ -18,7 +18,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:55:35.845+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "1", @@ -32,7 +33,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:39:31.557+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "2", @@ -46,7 +48,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:47:04.499+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "3", @@ -60,7 +63,8 @@ "Timeout": 30, "LastModified": "2016-02-29T14:58:08.443+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "4", @@ -74,7 +78,8 @@ "Timeout": 30, "LastModified": "2016-02-29T16:25:49.425+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "5", @@ -88,7 +93,8 @@ "Timeout": 30, "LastModified": "2016-02-29T16:34:47.988+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "6", @@ -102,7 +108,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:12:28.708+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "7", @@ -116,7 +123,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:24:01.446+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" }, { "Version": "8", @@ -130,7 +138,8 @@ "Timeout": 30, "LastModified": "2016-03-01T00:55:35.845+0000", "Runtime": "python2.7", - "Description": "Zappa Deployment" + "Description": "Zappa Deployment", + "PackageType": "Zip" } ] } diff --git a/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_1.json b/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_1.json new file mode 100644 index 000000000..1e4ff953f --- /dev/null +++ b/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_1.json @@ -0,0 +1,128 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "786c144e-e22e-11e5-86f4-4528d3e5269b" + }, + "Versions": [ + { + "Version": "$LATEST", + "CodeSha256": "qPyTWxngvz505vUX4v0IbW4H6CdQCSiYSGPsNQHPNTc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154778, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:$LATEST", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:55:35.845+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "1", + "CodeSha256": "eEW6OoZIm1g+3sLnoGqkfG9PcvYcXYoTd8uZYCMhA2U=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12102921, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:1", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:39:31.557+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "2", + "CodeSha256": "QXwkHDWCmczXTdDi9AYf6Ws74jkAI6zUJ+tf7s5zLzk=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12152312, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:2", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:47:04.499+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "3", + "CodeSha256": "Hpt/J6gyhnYXm7NVWwzomJoExr0i9II//4zaLm0DYgc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12152182, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:3", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:58:08.443+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "4", + "CodeSha256": "1ofpfK2Jonu13iPUBMeNYR962iWcD5vL1YrJazF9tF0=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154467, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:4", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T16:25:49.425+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "5", + "CodeSha256": "bwLhQSjeZOmNC10s2eMO0ljWJBNX5tT4S9MgkvBWjOM=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154507, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:5", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T16:34:47.988+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "6", + "CodeSha256": "uxHYC6Uf/sUl884TWUqlwi0WWE+ixfxhO964yPec5zM=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154780, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:6", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:12:28.708+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "7", + "CodeSha256": "t3pcqjdrLOqd2p0bRsCEOhgvLJ9sLJrVazpGqhDybsc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154784, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:7", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:24:01.446+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "8", + "CodeSha256": "qPyTWxngvz505vUX4v0IbW4H6CdQCSiYSGPsNQHPNTc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154778, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:8", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:55:35.845+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + } + ] + } +} diff --git a/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_2.json b/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_2.json new file mode 100644 index 000000000..cfe367df5 --- /dev/null +++ b/tests/placebo/TestZappa.test_rollback_lambda_function_version_docker/lambda.ListVersionsByFunction_2.json @@ -0,0 +1,128 @@ +{ + "status_code": 200, + "data": { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "78c11231-e22e-11e5-bb00-6935e1456f05" + }, + "Versions": [ + { + "Version": "$LATEST", + "CodeSha256": "qPyTWxngvz505vUX4v0IbW4H6CdQCSiYSGPsNQHPNTc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154778, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:$LATEST", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:55:35.845+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "1", + "CodeSha256": "eEW6OoZIm1g+3sLnoGqkfG9PcvYcXYoTd8uZYCMhA2U=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12102921, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:1", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:39:31.557+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "2", + "CodeSha256": "QXwkHDWCmczXTdDi9AYf6Ws74jkAI6zUJ+tf7s5zLzk=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12152312, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:2", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:47:04.499+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "3", + "CodeSha256": "Hpt/J6gyhnYXm7NVWwzomJoExr0i9II//4zaLm0DYgc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12152182, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:3", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T14:58:08.443+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "4", + "CodeSha256": "1ofpfK2Jonu13iPUBMeNYR962iWcD5vL1YrJazF9tF0=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154467, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:4", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T16:25:49.425+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "5", + "CodeSha256": "bwLhQSjeZOmNC10s2eMO0ljWJBNX5tT4S9MgkvBWjOM=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154507, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:5", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-02-29T16:34:47.988+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "6", + "CodeSha256": "uxHYC6Uf/sUl884TWUqlwi0WWE+ixfxhO964yPec5zM=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154780, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:6", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:12:28.708+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "7", + "CodeSha256": "t3pcqjdrLOqd2p0bRsCEOhgvLJ9sLJrVazpGqhDybsc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154784, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:7", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:24:01.446+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + }, + { + "Version": "8", + "CodeSha256": "qPyTWxngvz505vUX4v0IbW4H6CdQCSiYSGPsNQHPNTc=", + "FunctionName": "django-helloworld-unicode", + "MemorySize": 512, + "CodeSize": 12154778, + "FunctionArn": "arn:aws:lambda:us-east-1:724336686645:function:django-helloworld-unicode:8", + "Role": "arn:aws:iam::724336686645:role/ZappaLambdaExecution", + "Timeout": 30, + "LastModified": "2016-03-01T00:55:35.845+0000", + "Description": "Zappa Deployment", + "PackageType": "Image" + } + ] + } +} diff --git a/tests/tests.py b/tests/tests.py index dff19705b..5b35e19e2 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -578,9 +578,10 @@ def test_update_aws_env_vars(self): with mock.patch.object(z, "lambda_client") as mock_client: # Simulate already having some AWS env vars remotely mock_client.get_function_configuration.return_value = { + "PackageType": "Zip", "Environment": { "Variables": {"REMOTE_ONLY": "AAA", "CHANGED_REMOTE": "BBB"} - } + }, } z.update_lambda_configuration( "test", @@ -601,9 +602,10 @@ def test_update_aws_env_vars(self): with mock.patch.object(z, "lambda_client") as mock_client: # Simulate already having some AWS env vars remotely but none set in aws_environment_variables mock_client.get_function_configuration.return_value = { + "PackageType": "Zip", "Environment": { "Variables": {"REMOTE_ONLY_1": "AAA", "REMOTE_ONLY_2": "BBB"} - } + }, } z.update_lambda_configuration("test", "test", "test") end_result_should_be = {"REMOTE_ONLY_1": "AAA", "REMOTE_ONLY_2": "BBB"} @@ -617,7 +619,7 @@ def test_update_layers(self): z.credentials_arn = object() with mock.patch.object(z, "lambda_client") as mock_client: - mock_client.get_function_configuration.return_value = {} + mock_client.get_function_configuration.return_value = {"PackageType": "Zip"} z.update_lambda_configuration( "test", "test", "test", layers=["Layer1", "Layer2"] ) @@ -626,7 +628,7 @@ def test_update_layers(self): ["Layer1", "Layer2"], ) with mock.patch.object(z, "lambda_client") as mock_client: - mock_client.get_function_configuration.return_value = {} + mock_client.get_function_configuration.return_value = {"PackageType": "Zip"} z.update_lambda_configuration("test", "test", "test") self.assertEqual( mock_client.update_function_configuration.call_args[1]["Layers"], [] @@ -638,7 +640,7 @@ def test_update_empty_aws_env_hash(self): with mock.patch.object(z, "lambda_client") as mock_client: # Simulate having no AWS env vars remotely - mock_client.get_function_configuration.return_value = {} + mock_client.get_function_configuration.return_value = {"PackageType": "Zip"} z.update_lambda_configuration( "test", "test", @@ -1253,6 +1255,21 @@ def test_cli_colorize_invoke_command_bad_string(self): colorized_string = zappa_cli.colorize_invoke_command(plain_string) self.assertEqual(final_string, colorized_string) + def test_cli_save_python_settings_file(self): + zappa_cli = ZappaCLI() + zappa_cli.api_stage = "ttt888" + zappa_cli.load_settings("test_settings.json") + + temp_dir = tempfile.mkdtemp() + good_output_path = os.path.join(temp_dir, "zappa_settings.py") + assert not os.path.exists(good_output_path) + zappa_cli.save_python_settings_file(good_output_path) + assert os.path.exists(good_output_path) + + bad_output_path = os.path.join(temp_dir, "settings.py") + with self.assertRaises(ValueError): + zappa_cli.save_python_settings_file(bad_output_path) + # def test_cli_args(self): # zappa_cli = ZappaCLI() # # Sanity diff --git a/tests/tests_placebo.py b/tests/tests_placebo.py index c203509b0..8836e18e1 100644 --- a/tests/tests_placebo.py +++ b/tests/tests_placebo.py @@ -128,6 +128,28 @@ def test_create_lambda_function_local(self, session): function_name="test_lmbda_function55", ) + @placebo_session + def test_create_lambda_function_docker(self, session): + bucket_name = "lmbda" + docker_image_uri = "docker_image_uri" + + z = Zappa(session) + z.aws_region = "us-east-1" + z.load_credentials(session) + z.credentials_arn = "arn:aws:iam::12345:role/ZappaLambdaExecution" + + arn = z.create_lambda_function( + bucket=bucket_name, + docker_image_uri=docker_image_uri, + function_name="test_lmbda_function55", + ) + + arn = z.update_lambda_function( + bucket=bucket_name, + docker_image_uri=docker_image_uri, + function_name="test_lmbda_function55", + ) + @placebo_session def test_rollback_lambda_function_version(self, session): z = Zappa(session) @@ -139,6 +161,23 @@ def test_rollback_lambda_function_version(self, session): function_arn = z.rollback_lambda_function_version(function_name, 1) + @placebo_session + def test_rollback_lambda_function_version_docker(self, session): + z = Zappa(session) + z.credentials_arn = "arn:aws:iam::724336686645:role/ZappaLambdaExecution" + + function_name = "django-helloworld-unicode" + + with self.assertRaises(NotImplementedError): + z.rollback_lambda_function_version(function_name) + + @placebo_session + def test_is_lambda_function_ready(self, session): + z = Zappa(session) + z.credentials_arn = "arn:aws:iam::724336686645:role/ZappaLambdaExecution" + function_name = "django-helloworld-unicode" + z.is_lambda_function_ready(function_name) + @placebo_session def test_invoke_lambda_function(self, session): z = Zappa(session) diff --git a/zappa/cli.py b/zappa/cli.py index 9eb6a00a8..6ba6dd5fe 100755 --- a/zappa/cli.py +++ b/zappa/cli.py @@ -83,7 +83,6 @@ class ZappaCLI: """ ZappaCLI object is responsible for loading the settings, handling the input arguments and executing the calls to the core library. - """ # CLI @@ -198,9 +197,7 @@ def override_stage_config_setting(self, key, val): def handle(self, argv=None): """ Main function. - Parses command, load settings and dispatches accordingly. - """ desc = "Zappa - Deploy Python applications to AWS Lambda" " and API Gateway.\n" @@ -277,6 +274,11 @@ def handle(self, argv=None): "--zip", help="Deploy Lambda with specific local or S3 hosted zip package", ) + deploy_parser.add_argument( + "-d", + "--docker-image-uri", + help="Deploy Lambda with a specific docker image hosted in AWS Elastic Container Registry", + ) ## # Init @@ -478,6 +480,11 @@ def positive_int(s): "--no-upload", help="Update configuration where appropriate, but don't upload new code", ) + update_parser.add_argument( + "-d", + "--docker-image-uri", + help="Update Lambda with a specific docker image hosted in AWS Elastic Container Registry", + ) ## # Debug @@ -488,6 +495,24 @@ def positive_int(s): help="A debug shell with a loaded Zappa object.", ) + ## + # Python Settings File + ## + settings_parser = subparsers.add_parser( + "save-python-settings-file", + parents=[env_parser], + help="Generate & save the Zappa settings Python file for docker deployments", + ) + settings_parser.add_argument( + "-o", + "--output_path", + help=( + "The path to save the Zappa settings Python file. " + "File must be named zappa_settings.py and should be saved " + "in the same directory as the Zappa handler.py" + ), + ) + argcomplete.autocomplete(parser) args = parser.parse_args(argv) self.vargs = vars(args) @@ -602,7 +627,7 @@ def dispatch_command(self, command, stage): # Hand it off if command == "deploy": # pragma: no cover - self.deploy(self.vargs["zip"]) + self.deploy(self.vargs["zip"], self.vargs["docker_image_uri"]) if command == "package": # pragma: no cover self.package(self.vargs["output"]) if command == "template": # pragma: no cover @@ -613,7 +638,11 @@ def dispatch_command(self, command, stage): json=self.vargs["json"], ) elif command == "update": # pragma: no cover - self.update(self.vargs["zip"], self.vargs["no_upload"]) + self.update( + self.vargs["zip"], + self.vargs["no_upload"], + self.vargs["docker_image_uri"], + ) elif command == "rollback": # pragma: no cover self.rollback(self.vargs["num_rollback"]) elif command == "invoke": # pragma: no cover @@ -676,11 +705,26 @@ def dispatch_command(self, command, stage): self.certify(no_confirm=self.vargs["yes"], manual=self.vargs["manual"]) elif command == "shell": # pragma: no cover self.shell() + elif command == "save-python-settings-file": # pragma: no cover + self.save_python_settings_file(self.vargs["output_path"]) ## # The Commands ## + def save_python_settings_file(self, output_path=None): + settings_path = output_path or "zappa_settings.py" + print( + "Generating Zappa settings Python file and saving to {}".format( + settings_path + ) + ) + if not settings_path.endswith("zappa_settings.py"): + raise ValueError("Settings file must be named zappa_settings.py") + zappa_settings_s = self.get_zappa_settings_string() + with open(settings_path, "w") as f_out: + f_out.write(zappa_settings_s) + def package(self, output=None): """ Only build the package @@ -752,34 +796,13 @@ def template(self, lambda_arn, role_arn, output=None, json=False): with open(template_file, "r") as out: print(out.read()) - def deploy(self, source_zip=None): + def deploy(self, source_zip=None, docker_image_uri=None): """ Package your project, upload it to S3, register the Lambda function and create the API Gateway routes. - """ - if not source_zip: - # Make sure we're in a venv. - self.check_venv() - - # Execute the prebuild script - if self.prebuild_script: - self.execute_prebuild_script() - - # Make sure this isn't already deployed. - deployed_versions = self.zappa.get_lambda_function_versions( - self.lambda_name - ) - if len(deployed_versions) > 0: - raise ClickException( - "This application is " - + click.style("already deployed", fg="red") - + " - did you mean to call " - + click.style("update", bold=True) - + "?" - ) - + if not source_zip or docker_image_uri: # Make sure the necessary IAM execution roles are available if self.manage_roles: try: @@ -804,6 +827,25 @@ def deploy(self, source_zip=None): + "\n" ) + # Make sure this isn't already deployed. + deployed_versions = self.zappa.get_lambda_function_versions(self.lambda_name) + if len(deployed_versions) > 0: + raise ClickException( + "This application is " + + click.style("already deployed", fg="red") + + " - did you mean to call " + + click.style("update", bold=True) + + "?" + ) + + if not source_zip and not docker_image_uri: + # Make sure we're in a venv. + self.check_venv() + + # Execute the prebuild script + if self.prebuild_script: + self.execute_prebuild_script() + # Create the Lambda Zip self.create_package() self.callback("zip") @@ -868,18 +910,18 @@ def deploy(self, source_zip=None): layers=self.layers, concurrency=self.lambda_concurrency, ) - if source_zip and source_zip.startswith("s3://"): + kwargs["function_name"] = self.lambda_name + if docker_image_uri: + kwargs["docker_image_uri"] = docker_image_uri + elif source_zip and source_zip.startswith("s3://"): bucket, key_name = parse_s3_url(source_zip) - kwargs["function_name"] = self.lambda_name kwargs["bucket"] = bucket kwargs["s3_key"] = key_name elif source_zip and not source_zip.startswith("s3://"): with open(source_zip, mode="rb") as fh: byte_stream = fh.read() - kwargs["function_name"] = self.lambda_name kwargs["local_zip"] = byte_stream else: - kwargs["function_name"] = self.lambda_name kwargs["bucket"] = self.s3_bucket_name kwargs["s3_key"] = handler_file @@ -952,27 +994,30 @@ def deploy(self, source_zip=None): ) if self.stage_config.get("touch", True): + self.zappa.wait_until_lambda_function_is_ready( + function_name=self.lambda_name + ) self.touch_endpoint(endpoint_url) # Finally, delete the local copy our zip package - if not source_zip: + if not source_zip and not docker_image_uri: if self.stage_config.get("delete_local_zip", True): self.remove_local_zip() # Remove the project zip from S3. - if not source_zip: + if not source_zip and not docker_image_uri: self.remove_uploaded_zip() self.callback("post") click.echo(deployment_string) - def update(self, source_zip=None, no_upload=False): + def update(self, source_zip=None, no_upload=False, docker_image_uri=None): """ Repackage and update the function code. """ - if not source_zip: + if not source_zip and not docker_image_uri: # Make sure we're in a venv. self.check_venv() @@ -1090,7 +1135,13 @@ def update(self, source_zip=None, no_upload=False): num_revisions=self.num_retained_versions, concurrency=self.lambda_concurrency, ) - if source_zip and source_zip.startswith("s3://"): + if docker_image_uri: + kwargs["docker_image_uri"] = docker_image_uri + self.lambda_arn = self.zappa.update_lambda_function(**kwargs) + self.zappa.wait_until_lambda_function_is_ready( + function_name=self.lambda_name + ) + elif source_zip and source_zip.startswith("s3://"): bucket, key_name = parse_s3_url(source_zip) kwargs.update(dict(bucket=bucket, s3_key=key_name)) self.lambda_arn = self.zappa.update_lambda_function(**kwargs) @@ -1105,7 +1156,7 @@ def update(self, source_zip=None, no_upload=False): self.lambda_arn = self.zappa.update_lambda_function(**kwargs) # Remove the uploaded zip from S3, because it is now registered.. - if not source_zip and not no_upload: + if not source_zip and not no_upload and not docker_image_uri: self.remove_uploaded_zip() # Update the configuration, in case there are changes. @@ -1124,7 +1175,7 @@ def update(self, source_zip=None, no_upload=False): ) # Finally, delete the local copy our zip package - if not source_zip and not no_upload: + if not source_zip and not no_upload and not docker_image_uri: if self.stage_config.get("delete_local_zip", True): self.remove_local_zip() @@ -1210,6 +1261,9 @@ def update(self, source_zip=None, no_upload=False): deployed_string = deployed_string + " (" + api_url + ")" if self.stage_config.get("touch", True): + self.zappa.wait_until_lambda_function_is_ready( + function_name=self.lambda_name + ) if api_url: self.touch_endpoint(api_url) elif endpoint_url: @@ -1242,7 +1296,6 @@ def tail( ): """ Tail this function's logs. - if keep_open, do so repeatedly, printing any new logs """ @@ -1328,7 +1381,6 @@ def schedule(self): """ Given a a list of functions and a schedule to execute them, setup up regular execution. - """ events = self.stage_config.get("events", []) @@ -1422,7 +1474,6 @@ def unschedule(self): """ Given a a list of scheduled functions, tear down their regular execution. - """ # Run even if events are not defined to remove previously existing ones (thus default to []). @@ -1523,7 +1574,6 @@ def colorize_invoke_command(self, string): """ Apply various heuristics to return a colorized version the invoke command string. If these fail, simply return the string in plaintext. - Inspired by colorize_log_entry(). """ @@ -1616,13 +1666,15 @@ def tabular_print(title, value): status_dict["Lambda Name"] = self.lambda_name status_dict["Lambda ARN"] = self.lambda_arn status_dict["Lambda Role ARN"] = conf["Role"] - status_dict["Lambda Handler"] = conf["Handler"] status_dict["Lambda Code Size"] = conf["CodeSize"] status_dict["Lambda Version"] = conf["Version"] status_dict["Lambda Last Modified"] = conf["LastModified"] status_dict["Lambda Memory Size"] = conf["MemorySize"] status_dict["Lambda Timeout"] = conf["Timeout"] - status_dict["Lambda Runtime"] = conf["Runtime"] + # Handler & Runtime won't be present for lambda Docker deployments + # https://github.com/Miserlou/Zappa/issues/2188 + status_dict["Lambda Handler"] = conf.get("Handler", "") + status_dict["Lambda Runtime"] = conf.get("Runtime", "") if "VpcConfig" in conf.keys(): status_dict["Lambda VPC ID"] = conf.get("VpcConfig", {}).get( "VpcId", "Not assigned" @@ -1726,7 +1778,6 @@ def tabular_print(title, value): def check_stage_name(self, stage_name): """ Make sure the stage name matches the AWS-allowed pattern - (calls to apigateway_client.create_deployment, will fail with error message "ClientError: An error occurred (BadRequestException) when calling the CreateDeployment operation: Stage name only allows @@ -1739,7 +1790,6 @@ def check_stage_name(self, stage_name): def check_environment(self, environment): """ Make sure the environment contains only strings - (since putenv needs a string) """ @@ -1759,10 +1809,8 @@ def check_environment(self, environment): def init(self, settings_file="zappa_settings.json"): """ Initialize a new Zappa project by creating a new zappa_settings.json in a guided process. - This should probably be broken up into few separate componants once it's stable. Testing these inputs requires monkeypatching with mock, which isn't pretty. - """ # Make sure we're in a venv. @@ -2331,7 +2379,6 @@ def shell(self): def callback(self, position): """ Allows the execution of custom code between creation of the zip file and deployment to AWS. - :return: None """ @@ -2417,9 +2464,7 @@ def check_for_update(self): def load_settings(self, settings_file=None, session=None): """ Load the local zappa_settings file. - An existing boto session can be supplied, though this is likely for testing purposes. - Returns the loaded Zappa object. """ @@ -2673,7 +2718,6 @@ def create_package(self, output=None): """ Ensure that the package can be properly configured, and then create it. - """ # Create the Lambda zip package (includes project and virtualenvironment) @@ -2750,191 +2794,186 @@ def create_package(self, output=None): with zipfile.ZipFile(handler_zip, "a") as lambda_zip: - settings_s = "# Generated by Zappa\n" - - if self.app_function: - if "." not in self.app_function: # pragma: no cover - raise ClickException( - "Your " - + click.style("app_function", fg="red", bold=True) - + " value is not a modular path." - + " It needs to be in the format `" - + click.style("your_module.your_app_object", bold=True) - + "`." - ) - app_module, app_function = self.app_function.rsplit(".", 1) - settings_s = ( - settings_s - + "APP_MODULE='{0!s}'\nAPP_FUNCTION='{1!s}'\n".format( - app_module, app_function - ) - ) + settings_s = self.get_zappa_settings_string() - if self.exception_handler: - settings_s += "EXCEPTION_HANDLER='{0!s}'\n".format( - self.exception_handler - ) - else: - settings_s += "EXCEPTION_HANDLER=None\n" - - if self.debug: - settings_s = settings_s + "DEBUG=True\n" - else: - settings_s = settings_s + "DEBUG=False\n" + # Copy our Django app into root of our package. + # It doesn't work otherwise. + if self.django_settings: + base = __file__.rsplit(os.sep, 1)[0] + django_py = "".join(os.path.join(base, "ext", "django_zappa.py")) + lambda_zip.write(django_py, "django_zappa_app.py") - settings_s = settings_s + "LOG_LEVEL='{0!s}'\n".format((self.log_level)) + # Lambda requires a specific chmod + temp_settings = tempfile.NamedTemporaryFile(delete=False) + os.chmod(temp_settings.name, 0o644) + temp_settings.write(bytes(settings_s, "utf-8")) + temp_settings.close() + lambda_zip.write(temp_settings.name, "zappa_settings.py") + os.unlink(temp_settings.name) - if self.binary_support: - settings_s = settings_s + "BINARY_SUPPORT=True\n" - else: - settings_s = settings_s + "BINARY_SUPPORT=False\n" + def get_zappa_settings_string(self): + settings_s = "# Generated by Zappa\n" - head_map_dict = {} - head_map_dict.update(dict(self.context_header_mappings)) - settings_s = settings_s + "CONTEXT_HEADER_MAPPINGS={0}\n".format( - head_map_dict + if self.app_function: + if "." not in self.app_function: # pragma: no cover + raise ClickException( + "Your " + + click.style("app_function", fg="red", bold=True) + + " value is not a modular path." + + " It needs to be in the format `" + + click.style("your_module.your_app_object", bold=True) + + "`." + ) + app_module, app_function = self.app_function.rsplit(".", 1) + settings_s = ( + settings_s + + "APP_MODULE='{0!s}'\nAPP_FUNCTION='{1!s}'\n".format( + app_module, app_function + ) ) - # If we're on a domain, we don't need to define the /<> in - # the WSGI PATH - if self.domain: - settings_s = settings_s + "DOMAIN='{0!s}'\n".format((self.domain)) - else: - settings_s = settings_s + "DOMAIN=None\n" + if self.exception_handler: + settings_s += "EXCEPTION_HANDLER='{0!s}'\n".format(self.exception_handler) + else: + settings_s += "EXCEPTION_HANDLER=None\n" - if self.base_path: - settings_s = settings_s + "BASE_PATH='{0!s}'\n".format((self.base_path)) - else: - settings_s = settings_s + "BASE_PATH=None\n" - - # Pass through remote config bucket and path - if self.remote_env: - settings_s = settings_s + "REMOTE_ENV='{0!s}'\n".format(self.remote_env) - # DEPRECATED. use remove_env instead - elif self.remote_env_bucket and self.remote_env_file: - settings_s = settings_s + "REMOTE_ENV='s3://{0!s}/{1!s}'\n".format( - self.remote_env_bucket, self.remote_env_file - ) + if self.debug: + settings_s = settings_s + "DEBUG=True\n" + else: + settings_s = settings_s + "DEBUG=False\n" - # Local envs - env_dict = {} - if self.aws_region: - env_dict["AWS_REGION"] = self.aws_region - env_dict.update(dict(self.environment_variables)) + settings_s = settings_s + "LOG_LEVEL='{0!s}'\n".format((self.log_level)) - # Environment variable keys must be ascii - # https://github.com/Miserlou/Zappa/issues/604 - # https://github.com/Miserlou/Zappa/issues/998 - try: - env_dict = dict( - (k.encode("ascii").decode("ascii"), v) - for (k, v) in env_dict.items() - ) - except Exception: - raise ValueError("Environment variable keys must be ascii.") + if self.binary_support: + settings_s = settings_s + "BINARY_SUPPORT=True\n" + else: + settings_s = settings_s + "BINARY_SUPPORT=False\n" - settings_s = settings_s + "ENVIRONMENT_VARIABLES={0}\n".format(env_dict) + head_map_dict = {} + head_map_dict.update(dict(self.context_header_mappings)) + settings_s = settings_s + "CONTEXT_HEADER_MAPPINGS={0}\n".format(head_map_dict) + + # If we're on a domain, we don't need to define the /<> in + # the WSGI PATH + if self.domain: + settings_s = settings_s + "DOMAIN='{0!s}'\n".format((self.domain)) + else: + settings_s = settings_s + "DOMAIN=None\n" - # We can be environment-aware - settings_s = settings_s + "API_STAGE='{0!s}'\n".format((self.api_stage)) - settings_s = settings_s + "PROJECT_NAME='{0!s}'\n".format( - (self.project_name) + if self.base_path: + settings_s = settings_s + "BASE_PATH='{0!s}'\n".format((self.base_path)) + else: + settings_s = settings_s + "BASE_PATH=None\n" + + # Pass through remote config bucket and path + if self.remote_env: + settings_s = settings_s + "REMOTE_ENV='{0!s}'\n".format(self.remote_env) + # DEPRECATED. use remove_env instead + elif self.remote_env_bucket and self.remote_env_file: + settings_s = settings_s + "REMOTE_ENV='s3://{0!s}/{1!s}'\n".format( + self.remote_env_bucket, self.remote_env_file + ) + + # Local envs + env_dict = {} + if self.aws_region: + env_dict["AWS_REGION"] = self.aws_region + env_dict.update(dict(self.environment_variables)) + + # Environment variable keys must be ascii + # https://github.com/Miserlou/Zappa/issues/604 + # https://github.com/Miserlou/Zappa/issues/998 + try: + env_dict = dict( + (k.encode("ascii").decode("ascii"), v) for (k, v) in env_dict.items() ) + except Exception: + raise ValueError("Environment variable keys must be ascii.") - if self.settings_file: - settings_s = settings_s + "SETTINGS_FILE='{0!s}'\n".format( - (self.settings_file) - ) - else: - settings_s = settings_s + "SETTINGS_FILE=None\n" + settings_s = settings_s + "ENVIRONMENT_VARIABLES={0}\n".format(env_dict) - if self.django_settings: - settings_s = settings_s + "DJANGO_SETTINGS='{0!s}'\n".format( - (self.django_settings) - ) - else: - settings_s = settings_s + "DJANGO_SETTINGS=None\n" + # We can be environment-aware + settings_s = settings_s + "API_STAGE='{0!s}'\n".format((self.api_stage)) + settings_s = settings_s + "PROJECT_NAME='{0!s}'\n".format((self.project_name)) - # If slim handler, path to project zip - if self.stage_config.get("slim_handler", False): - settings_s += "ARCHIVE_PATH='s3://{0!s}/{1!s}_{2!s}_current_project.tar.gz'\n".format( - self.s3_bucket_name, self.api_stage, self.project_name - ) + if self.settings_file: + settings_s = settings_s + "SETTINGS_FILE='{0!s}'\n".format( + (self.settings_file) + ) + else: + settings_s = settings_s + "SETTINGS_FILE=None\n" - # since includes are for slim handler add the setting here by joining arbitrary list from zappa_settings file - # and tell the handler we are the slim_handler - # https://github.com/Miserlou/Zappa/issues/776 - settings_s += "SLIM_HANDLER=True\n" - - include = self.stage_config.get("include", []) - if len(include) >= 1: - settings_s += "INCLUDE=" + str(include) + "\n" - - # AWS Events function mapping - event_mapping = {} - events = self.stage_config.get("events", []) - for event in events: - arn = event.get("event_source", {}).get("arn") - function = event.get("function") - if arn and function: - event_mapping[arn] = function - settings_s = settings_s + "AWS_EVENT_MAPPING={0!s}\n".format(event_mapping) - - # Map Lext bot events - bot_events = self.stage_config.get("bot_events", []) - bot_events_mapping = {} - for bot_event in bot_events: - event_source = bot_event.get("event_source", {}) - intent = event_source.get("intent") - invocation_source = event_source.get("invocation_source") - function = bot_event.get("function") - if intent and invocation_source and function: - bot_events_mapping[ - str(intent) + ":" + str(invocation_source) - ] = function - - settings_s = settings_s + "AWS_BOT_EVENT_MAPPING={0!s}\n".format( - bot_events_mapping - ) - - # Map cognito triggers - cognito_trigger_mapping = {} - cognito_config = self.stage_config.get("cognito", {}) - triggers = cognito_config.get("triggers", []) - for trigger in triggers: - source = trigger.get("source") - function = trigger.get("function") - if source and function: - cognito_trigger_mapping[source] = function - settings_s = settings_s + "COGNITO_TRIGGER_MAPPING={0!s}\n".format( - cognito_trigger_mapping - ) - - # Authorizer config - authorizer_function = self.authorizer.get("function", None) - if authorizer_function: - settings_s += "AUTHORIZER_FUNCTION='{0!s}'\n".format( - authorizer_function - ) + if self.django_settings: + settings_s = settings_s + "DJANGO_SETTINGS='{0!s}'\n".format( + (self.django_settings) + ) + else: + settings_s = settings_s + "DJANGO_SETTINGS=None\n" - # Copy our Django app into root of our package. - # It doesn't work otherwise. - if self.django_settings: - base = __file__.rsplit(os.sep, 1)[0] - django_py = "".join(os.path.join(base, "ext", "django_zappa.py")) - lambda_zip.write(django_py, "django_zappa_app.py") + # If slim handler, path to project zip + if self.stage_config.get("slim_handler", False): + settings_s += "ARCHIVE_PATH='s3://{0!s}/{1!s}_{2!s}_current_project.tar.gz'\n".format( + self.s3_bucket_name, self.api_stage, self.project_name + ) - # async response - async_response_table = self.stage_config.get("async_response_table", "") - settings_s += "ASYNC_RESPONSE_TABLE='{0!s}'\n".format(async_response_table) + # since includes are for slim handler add the setting here by joining arbitrary list from zappa_settings file + # and tell the handler we are the slim_handler + # https://github.com/Miserlou/Zappa/issues/776 + settings_s += "SLIM_HANDLER=True\n" - # Lambda requires a specific chmod - temp_settings = tempfile.NamedTemporaryFile(delete=False) - os.chmod(temp_settings.name, 0o644) - temp_settings.write(bytes(settings_s, "utf-8")) - temp_settings.close() - lambda_zip.write(temp_settings.name, "zappa_settings.py") - os.unlink(temp_settings.name) + include = self.stage_config.get("include", []) + if len(include) >= 1: + settings_s += "INCLUDE=" + str(include) + "\n" + + # AWS Events function mapping + event_mapping = {} + events = self.stage_config.get("events", []) + for event in events: + arn = event.get("event_source", {}).get("arn") + function = event.get("function") + if arn and function: + event_mapping[arn] = function + settings_s = settings_s + "AWS_EVENT_MAPPING={0!s}\n".format(event_mapping) + + # Map Lext bot events + bot_events = self.stage_config.get("bot_events", []) + bot_events_mapping = {} + for bot_event in bot_events: + event_source = bot_event.get("event_source", {}) + intent = event_source.get("intent") + invocation_source = event_source.get("invocation_source") + function = bot_event.get("function") + if intent and invocation_source and function: + bot_events_mapping[ + str(intent) + ":" + str(invocation_source) + ] = function + + settings_s = settings_s + "AWS_BOT_EVENT_MAPPING={0!s}\n".format( + bot_events_mapping + ) + + # Map cognito triggers + cognito_trigger_mapping = {} + cognito_config = self.stage_config.get("cognito", {}) + triggers = cognito_config.get("triggers", []) + for trigger in triggers: + source = trigger.get("source") + function = trigger.get("function") + if source and function: + cognito_trigger_mapping[source] = function + settings_s = settings_s + "COGNITO_TRIGGER_MAPPING={0!s}\n".format( + cognito_trigger_mapping + ) + + # Authorizer config + authorizer_function = self.authorizer.get("function", None) + if authorizer_function: + settings_s += "AUTHORIZER_FUNCTION='{0!s}'\n".format(authorizer_function) + + # async response + async_response_table = self.stage_config.get("async_response_table", "") + settings_s += "ASYNC_RESPONSE_TABLE='{0!s}'\n".format(async_response_table) + return settings_s def remove_local_zip(self): """ @@ -2979,7 +3018,6 @@ def print_logs( ): """ Parse, filter and print logs to the console. - """ for log in logs: @@ -3146,7 +3184,6 @@ def colorize_log_entry(self, string): def execute_prebuild_script(self): """ Parse and execute the prebuild_script from the zappa_settings. - """ (pb_mod_path, pb_func) = self.prebuild_script.rsplit(".", 1) @@ -3197,7 +3234,6 @@ def collision_warning(self, item): """ Given a string, print a warning if this could collide with a Zappa core package module. - Use for app functions and events. """ diff --git a/zappa/core.py b/zappa/core.py index 3b4a76445..d4737727e 100644 --- a/zappa/core.py +++ b/zappa/core.py @@ -249,9 +249,7 @@ class Zappa: """ Zappa! - Makes it easy to run Python web applications on AWS Lambda/API Gateway. - """ ## @@ -561,9 +559,7 @@ def create_lambda_zip( ): """ Create a Lambda-ready zip file of the current virtualenvironment and working directory. - Returns path to that file. - """ # Validate archive_format if archive_format not in ["zip", "tarball"]: @@ -995,10 +991,8 @@ def get_manylinux_wheel_url(self, package_name, package_version): """ For a given package name, returns a link to the download URL, else returns None. - Related: https://github.com/Miserlou/Zappa/issues/398 Examples here: https://gist.github.com/perrygeo/9545f94eaddec18a65fd7b56880adbae - This function downloads metadata JSON of `package_name` from Pypi and examines if the package has a manylinux wheel. This function also caches the JSON file so that we don't have to poll Pypi @@ -1048,9 +1042,7 @@ def upload_to_s3(self, source_path, bucket_name, disable_progress=False): r""" Given a file, upload it to S3. Credentials should be stored in environment variables or ~/.aws/credentials (%USERPROFILE%\.aws\credentials on Windows). - Returns True on success, false on failure. - """ try: self.s3_client.head_bucket(Bucket=bucket_name) @@ -1134,11 +1126,8 @@ def copy_on_s3(self, src_file_name, dst_file_name, bucket_name): def remove_from_s3(self, file_name, bucket_name): """ Given a file name and a bucket, remove it from S3. - There's no reason to keep the file hosted on S3 once its been made into a Lambda function, so we can delete it from S3. - Returns True on success, False on failure. - """ try: self.s3_client.head_bucket(Bucket=bucket_name) @@ -1182,6 +1171,7 @@ def create_lambda_function( use_alb=False, layers=None, concurrency=None, + docker_image_uri=None, ): """ Given a bucket and key (or a local path) of a valid Lambda-zip, a function name and a handler, register that Lambda function. @@ -1201,9 +1191,7 @@ def create_lambda_function( kwargs = dict( FunctionName=function_name, - Runtime=runtime, Role=self.credentials_arn, - Handler=handler, Description=description, Timeout=timeout, MemorySize=memory_size, @@ -1215,11 +1203,23 @@ def create_lambda_function( TracingConfig={"Mode": "Active" if self.xray_tracing else "PassThrough"}, Layers=layers, ) - if local_zip: + if not docker_image_uri: + kwargs["Runtime"] = runtime + kwargs["Handler"] = handler + kwargs["PackageType"] = "Zip" + + if docker_image_uri: + kwargs["Code"] = {"ImageUri": docker_image_uri} + # default is ZIP. override to Image for container support + kwargs["PackageType"] = "Image" + # The create function operation times out when this is '' (the default) + # So just remove it from the kwargs if it is not specified + if aws_kms_key_arn == "": + kwargs.pop("KMSKeyArn") + elif local_zip: kwargs["Code"] = {"ZipFile": local_zip} else: kwargs["Code"] = {"S3Bucket": bucket, "S3Key": s3_key} - response = self.lambda_client.create_function(**kwargs) resource_arn = response["FunctionArn"] version = response["Version"] @@ -1255,6 +1255,7 @@ def update_lambda_function( local_zip=None, num_revisions=None, concurrency=None, + docker_image_uri=None, ): """ Given a bucket and key (or a local path) of a valid Lambda-zip, a function name and a handler, update that Lambda function's code. @@ -1263,7 +1264,9 @@ def update_lambda_function( print("Updating Lambda function code..") kwargs = dict(FunctionName=function_name, Publish=publish) - if local_zip: + if docker_image_uri: + kwargs["ImageUri"] = docker_image_uri + elif local_zip: kwargs["ZipFile"] = local_zip else: kwargs["S3Bucket"] = bucket @@ -1372,20 +1375,24 @@ def update_lambda_configuration( if key not in aws_environment_variables: aws_environment_variables[key] = value - response = self.lambda_client.update_function_configuration( - FunctionName=function_name, - Runtime=runtime, - Role=self.credentials_arn, - Handler=handler, - Description=description, - Timeout=timeout, - MemorySize=memory_size, - VpcConfig=vpc_config, - Environment={"Variables": aws_environment_variables}, - KMSKeyArn=aws_kms_key_arn, - TracingConfig={"Mode": "Active" if self.xray_tracing else "PassThrough"}, - Layers=layers, - ) + kwargs = { + "FunctionName": function_name, + "Role": self.credentials_arn, + "Description": description, + "Timeout": timeout, + "MemorySize": memory_size, + "VpcConfig": vpc_config, + "Environment": {"Variables": aws_environment_variables}, + "KMSKeyArn": aws_kms_key_arn, + "TracingConfig": {"Mode": "Active" if self.xray_tracing else "PassThrough"}, + } + + if lambda_aws_config["PackageType"] != "Image": + kwargs.update( + {"Handler": handler, "Runtime": runtime, "Layers": layers,} + ) + + response = self.lambda_client.update_function_configuration(**kwargs) resource_arn = response["FunctionArn"] @@ -1419,13 +1426,21 @@ def rollback_lambda_function_version( ): """ Rollback the lambda function code 'versions_back' number of revisions. - Returns the Function ARN. """ response = self.lambda_client.list_versions_by_function( FunctionName=function_name ) + # https://github.com/Miserlou/Zappa/pull/2192 + if ( + len(response.get("Versions", [])) > 1 + and response["Versions"][-1]["PackageType"] == "Image" + ): + raise NotImplementedError( + "Zappa's rollback functionality is not available for Docker based deployments" + ) + # Take into account $LATEST if len(response["Versions"]) < versions_back + 1: print("We do not have {} revisions. Aborting".format(str(versions_back))) @@ -1459,10 +1474,38 @@ def rollback_lambda_function_version( return response["FunctionArn"] + def is_lambda_function_ready(self, function_name): + """ + Checks if a lambda function is active and no updates are in progress. + """ + response = self.lambda_client.get_function(FunctionName=function_name) + return ( + response["Configuration"]["State"] == "Active" + and response["Configuration"]["LastUpdateStatus"] != "InProgress" + ) + + def wait_until_lambda_function_is_ready(self, function_name): + """ + Continuously check if a lambda function is active. + For functions deployed with a docker image instead of a + ZIP package, the function can take a few seconds longer + to be created or update, so we must wait before running any status + checks against the function. + """ + show_waiting_message = True + while True: + if self.is_lambda_function_ready(function_name): + break + + if show_waiting_message: + print("Waiting until lambda function is ready.") + show_waiting_message = False + + time.sleep(1) + def get_lambda_function(self, function_name): """ Returns the lambda function ARN, given a name - This requires the "lambda:GetFunction" role. """ response = self.lambda_client.get_function(FunctionName=function_name) @@ -1471,7 +1514,6 @@ def get_lambda_function(self, function_name): def get_lambda_function_versions(self, function_name): """ Simply returns the versions available for a Lambda function, given a function name. - """ try: response = self.lambda_client.list_versions_by_function( @@ -1484,9 +1526,7 @@ def get_lambda_function_versions(self, function_name): def delete_lambda_function(self, function_name): """ Given a function name, delete it from AWS Lambda. - Returns the response. - """ print("Deleting Lambda function..") @@ -1738,7 +1778,6 @@ def create_api_gateway_routes( ): """ Create the API Gateway for this Zappa deployment. - Returns the new RestAPI CF resource. """ @@ -1987,7 +2026,6 @@ def deploy_api_gateway( ): """ Deploy the API Gateway! - Return the deployed API URL. """ print("Deploying API Gateway..") @@ -2649,13 +2687,10 @@ def update_domain_name( """ This updates your certificate information for an existing domain, with similar arguments to boto's update_domain_name API Gateway api. - It returns the resulting new domain information including the new certificate's ARN if created during this process. - Previously, this method involved downtime that could take up to 40 minutes because the API Gateway api only allowed this by deleting, and then creating it. - Related issues: https://github.com/Miserlou/Zappa/issues/590 https://github.com/Miserlou/Zappa/issues/588 https://github.com/Miserlou/Zappa/pull/458 @@ -2754,9 +2789,7 @@ def get_all_zones(self): def get_domain_name(self, domain_name, route53=True): """ Scan our hosted zones for the record of a given name. - Returns the record entry, else None. - """ # Make sure api gateway domain is present try: @@ -2806,7 +2839,6 @@ def get_domain_name(self, domain_name, route53=True): def get_credentials_arn(self): """ Given our role name, get and set the credentials_arn. - """ role = self.iam.Role(self.role_name) self.credentials_arn = role.arn @@ -2815,7 +2847,6 @@ def get_credentials_arn(self): def create_iam_roles(self): """ Create and defines the IAM roles and policies necessary for Zappa. - If the IAM role already exists, it will be updated if necessary. """ attach_policy_obj = json.loads(self.attach_policy) @@ -2907,7 +2938,6 @@ def _clear_policy(self, lambda_name): def create_event_permission(self, lambda_name, principal, source_arn): """ Create permissions to link to an event. - Related: http://docs.aws.amazon.com/lambda/latest/dg/with-s3-example-configure-event-source.html """ logger.debug( @@ -2932,10 +2962,8 @@ def create_event_permission(self, lambda_name, principal, source_arn): def schedule_events(self, lambda_arn, lambda_name, events, default=True): """ Given a Lambda ARN, name and a list of events, schedule this as CloudWatch Events. - 'events' is a list of dictionaries, where the dict must contains the string of a 'function' and the string of the event 'expression', and an optional 'name' and 'description'. - Expressions can be in rate or cron format: http://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html """ @@ -3130,7 +3158,6 @@ def get_scheduled_event_name(event, function, lambda_name, index=0): def get_event_name(lambda_name, name): """ Returns an AWS-valid Lambda event name. - """ return "{prefix:.{width}}-{postfix}".format( prefix=lambda_name, width=max(0, 63 - len(name)), postfix=name @@ -3151,10 +3178,8 @@ def get_hashed_rule_name(event, function, lambda_name): def delete_rule(self, rule_name): """ Delete a CWE rule. - This deletes them, but they will still show up in the AWS console. Annoying. - """ logger.debug("Deleting existing rule {}".format(rule_name)) @@ -3210,7 +3235,6 @@ def unschedule_events( excluded_source_services = excluded_source_services or [] """ Given a list of events, unschedule these CloudWatch Events. - 'events' is a list of dictionaries, where the dict must contains the string of a 'function' and the string of the event 'expression', and an optional 'name' and 'description'. """ @@ -3411,7 +3435,6 @@ def remove_api_gateway_logs(self, project_name): def get_hosted_zone_id_for_domain(self, domain): """ Get the Hosted Zone ID for a given domain. - """ all_zones = self.get_all_zones() return self.get_best_match_zone(all_zones, domain) @@ -3473,7 +3496,6 @@ def get_dns_challenge_change_batch(action, domain, txt_challenge): """ Given action, domain and challenge, return a change batch to use with route53 call. - :param action: DELETE | UPSERT :param domain: domain name :param txt_challenge: challenge @@ -3508,9 +3530,7 @@ def shell(self): def load_credentials(self, boto_session=None, profile_name=None): """ Load AWS credentials. - An optional boto_session can be provided, but that's usually for testing. - An optional profile_name can be provided for config files that have multiple sets of credentials. """ From 16d0ed67fc52be2c87cbb46e5804d692a2e296c9 Mon Sep 17 00:00:00 2001 From: ian-whitestone Date: Sun, 13 Jun 2021 16:27:28 -0400 Subject: [PATCH 3/3] Reformat files with black round 2 --- tests/test_handler.py | 30 +++++++--- tests/tests.py | 112 +++++++++++++++++++++++++++++--------- tests/tests_middleware.py | 4 +- tests/tests_placebo.py | 15 +++-- zappa/cli.py | 18 ++++-- zappa/core.py | 43 +++++++++++---- zappa/letsencrypt.py | 23 ++++++-- 7 files changed, 187 insertions(+), 58 deletions(-) diff --git a/tests/test_handler.py b/tests/test_handler.py index ad44aff34..1089ef798 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -82,7 +82,9 @@ def test_wsgi_script_name_on_aws_url(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": {"Host": "1234567890.execute-api.us-east-1.amazonaws.com",}, + "headers": { + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + }, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -108,7 +110,9 @@ def test_wsgi_script_name_on_domain_url(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": {"Host": "example.com",}, + "headers": { + "Host": "example.com", + }, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -131,7 +135,9 @@ def test_wsgi_script_name_with_multi_value_header(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "multiValueHeaders": {"Host": ["example.com"],}, + "multiValueHeaders": { + "Host": ["example.com"], + }, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -152,7 +158,9 @@ def test_wsgi_script_name_with_multi_value_querystring(self): "resource": "/{proxy+}", "requestContext": {}, "multiValueQueryStringParameters": {"multi": ["value", "qs"]}, - "multiValueHeaders": {"Host": ["example.com"],}, + "multiValueHeaders": { + "Host": ["example.com"], + }, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -200,7 +208,9 @@ def test_exception_handler_on_web_request(self): "resource": "/{proxy+}", "requestContext": {}, "queryStringParameters": {}, - "headers": {"Host": "1234567890.execute-api.us-east-1.amazonaws.com",}, + "headers": { + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + }, "pathParameters": {"proxy": "return/request/url"}, "httpMethod": "GET", "stageVariables": {}, @@ -254,7 +264,10 @@ def test_bot_triggered_event(self): "messageVersion": "1.0", "invocationSource": "DialogCodeHook", "userId": "user-id specified in the POST request to Amazon Lex.", - "sessionAttributes": {"key1": "value1", "key2": "value2",}, + "sessionAttributes": { + "key1": "value1", + "key2": "value2", + }, "bot": {"name": "bot-name", "alias": "bot-alias", "version": "bot-version"}, "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request", "currentIntent": { @@ -282,7 +295,10 @@ def test_exception_in_bot_triggered_event(self): "messageVersion": "1.0", "invocationSource": "DialogCodeHook", "userId": "user-id specified in the POST request to Amazon Lex.", - "sessionAttributes": {"key1": "value1", "key2": "value2",}, + "sessionAttributes": { + "key1": "value1", + "key2": "value2", + }, "bot": {"name": "bot-name", "alias": "bot-alias", "version": "bot-version"}, "outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request", "currentIntent": { diff --git a/tests/tests.py b/tests/tests.py index 5b35e19e2..52c3a6eb7 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -270,7 +270,10 @@ def test_getting_installed_packages_mixed_case_location(self, *args): z.get_installed_packages( "/venv/Site-packages", "/venv/site-packages64" ), - {"superpackage": "0.1", "superpackage64": "0.1",}, + { + "superpackage": "0.1", + "superpackage64": "0.1", + }, ) def test_getting_installed_packages_mixed_case(self, *args): @@ -1787,7 +1790,9 @@ def get_domain_name(domain, *_args, **_kwargs): # Test creating domain without Route53 zappa_cli.zappa_settings["stage"].update( - {"route53_enabled": False,} + { + "route53_enabled": False, + } ) zappa_cli.zappa.reset_mock() zappa_cli.zappa.domain_names["test.example.com"] = "" @@ -1881,7 +1886,10 @@ def test_get_all_zones_two_pages(self, client): zones = zappa_core.get_all_zones() zappa_core.route53.list_hosted_zones.assert_has_calls( - [mock.call(MaxItems="100"), mock.call(MaxItems="100", Marker="101"),] + [ + mock.call(MaxItems="100"), + mock.call(MaxItems="100", Marker="101"), + ] ) self.assertListEqual(zones["HostedZones"], [{"Id": "zone1"}, {"Id": "zone2"}]) @@ -2376,7 +2384,9 @@ def test_zappa_core_deploy_lambda_alb(self): ) elbv2_stubber.add_response( "describe_load_balancers", - expected_params={"LoadBalancerArns": [loadbalancer_arn],}, + expected_params={ + "LoadBalancerArns": [loadbalancer_arn], + }, service_response={ "LoadBalancers": [ {"LoadBalancerArn": loadbalancer_arn, "State": {"Code": "active"}} @@ -2400,8 +2410,17 @@ def test_zappa_core_deploy_lambda_alb(self): elbv2_stubber.add_response( "create_target_group", - expected_params={"Name": kwargs["lambda_name"], "TargetType": "lambda",}, - service_response={"TargetGroups": [{"TargetGroupArn": targetgroup_arn,}]}, + expected_params={ + "Name": kwargs["lambda_name"], + "TargetType": "lambda", + }, + service_response={ + "TargetGroups": [ + { + "TargetGroupArn": targetgroup_arn, + } + ] + }, ) elbv2_stubber.add_response( "modify_target_group_attributes", @@ -2443,10 +2462,15 @@ def test_zappa_core_deploy_lambda_alb(self): "create_listener", expected_params={ "Certificates": [ - {"CertificateArn": kwargs["alb_vpc_config"]["CertificateArn"],} + { + "CertificateArn": kwargs["alb_vpc_config"]["CertificateArn"], + } ], "DefaultActions": [ - {"Type": "forward", "TargetGroupArn": targetgroup_arn,} + { + "Type": "forward", + "TargetGroupArn": targetgroup_arn, + } ], "LoadBalancerArn": loadbalancer_arn, "Protocol": "HTTPS", @@ -2491,48 +2515,77 @@ def test_zappa_core_undeploy_lambda_alb(self): ) elbv2_stubber.add_response( "describe_load_balancers", - expected_params={"Names": [kwargs["lambda_name"]],}, + expected_params={ + "Names": [kwargs["lambda_name"]], + }, service_response={ - "LoadBalancers": [{"LoadBalancerArn": loadbalancer_arn,}] + "LoadBalancers": [ + { + "LoadBalancerArn": loadbalancer_arn, + } + ] }, ) elbv2_stubber.add_response( "describe_listeners", - expected_params={"LoadBalancerArn": loadbalancer_arn,}, - service_response={"Listeners": [{"ListenerArn": listener_arn,}]}, + expected_params={ + "LoadBalancerArn": loadbalancer_arn, + }, + service_response={ + "Listeners": [ + { + "ListenerArn": listener_arn, + } + ] + }, ) elbv2_stubber.add_response( "delete_listener", - expected_params={"ListenerArn": listener_arn,}, + expected_params={ + "ListenerArn": listener_arn, + }, service_response={}, ) elbv2_stubber.add_response( "delete_load_balancer", - expected_params={"LoadBalancerArn": loadbalancer_arn,}, + expected_params={ + "LoadBalancerArn": loadbalancer_arn, + }, service_response={}, ) elbv2_stubber.add_client_error( - "describe_load_balancers", service_error_code="LoadBalancerNotFound", + "describe_load_balancers", + service_error_code="LoadBalancerNotFound", ) lambda_stubber.add_response( "get_function", - expected_params={"FunctionName": kwargs["lambda_name"],}, + expected_params={ + "FunctionName": kwargs["lambda_name"], + }, service_response={"Configuration": {"FunctionArn": function_arn}}, ) elbv2_stubber.add_response( "describe_target_groups", - expected_params={"Names": [kwargs["lambda_name"]],}, - service_response={"TargetGroups": [{"TargetGroupArn": targetgroup_arn}],}, + expected_params={ + "Names": [kwargs["lambda_name"]], + }, + service_response={ + "TargetGroups": [{"TargetGroupArn": targetgroup_arn}], + }, ) elbv2_stubber.add_response( - "deregister_targets", service_response={}, + "deregister_targets", + service_response={}, ) elbv2_stubber.add_client_error( - "describe_target_health", service_error_code="InvalidTarget", + "describe_target_health", + service_error_code="InvalidTarget", ) elbv2_stubber.add_response( "delete_target_group", - expected_params={"TargetGroupArn": targetgroup_arn,}, + expected_params={ + "TargetGroupArn": targetgroup_arn, + }, service_response={}, ) lambda_stubber.activate() @@ -2552,9 +2605,12 @@ def test_set_lambda_concurrency(self, client): "FunctionArn": "abc", "Version": 1, } - access_logging_patch = zappa_core.create_lambda_function(concurrency=5,) + access_logging_patch = zappa_core.create_lambda_function( + concurrency=5, + ) boto_mock.client().put_function_concurrency.assert_called_with( - FunctionName="abc", ReservedConcurrentExecutions=5, + FunctionName="abc", + ReservedConcurrentExecutions=5, ) @mock.patch("botocore.client") @@ -2571,10 +2627,13 @@ def test_update_lambda_concurrency(self, client): "Version": 1, } access_logging_patch = zappa_core.update_lambda_function( - bucket="test", function_name="abc", concurrency=5, + bucket="test", + function_name="abc", + concurrency=5, ) boto_mock.client().put_function_concurrency.assert_called_with( - FunctionName="abc", ReservedConcurrentExecutions=5, + FunctionName="abc", + ReservedConcurrentExecutions=5, ) boto_mock.client().delete_function_concurrency.assert_not_called() @@ -2592,7 +2651,8 @@ def test_delete_lambda_concurrency(self, client): "Version": 1, } access_logging_patch = zappa_core.update_lambda_function( - bucket="test", function_name="abc", + bucket="test", + function_name="abc", ) boto_mock.client().put_function_concurrency.assert_not_called() boto_mock.client().delete_function_concurrency.assert_called_with( diff --git a/tests/tests_middleware.py b/tests/tests_middleware.py index 40aff46e7..81d82128d 100644 --- a/tests/tests_middleware.py +++ b/tests/tests_middleware.py @@ -161,7 +161,9 @@ def test_wsgi_map_context_headers_handling(self): "body": {}, "headers": {"Content-Type": "application/json"}, "pathParameters": {"proxy": "v1/runs"}, - "requestContext": {"authorizer": {"principalId": "user1"},}, + "requestContext": { + "authorizer": {"principalId": "user1"}, + }, "query": {}, } diff --git a/tests/tests_placebo.py b/tests/tests_placebo.py index 8836e18e1..91783c055 100644 --- a/tests/tests_placebo.py +++ b/tests/tests_placebo.py @@ -49,7 +49,8 @@ def test_upload_remove_s3(self, session): # will throw ClientError with 404 if object doesn't exist s3.meta.client.head_object( - Bucket=bucket_name, Key=zip_path, + Bucket=bucket_name, + Key=zip_path, ) res = z.remove_from_s3(zip_path, bucket_name) self.assertTrue(res) @@ -77,7 +78,8 @@ def test_copy_on_s3(self, session): # will throw ClientError with 404 if object doesn't exist s3.meta.client.head_object( - Bucket=bucket_name, Key=zip_path, + Bucket=bucket_name, + Key=zip_path, ) zp = "copy_" + zip_path res = z.copy_on_s3(zip_path, zp, bucket_name) @@ -102,7 +104,9 @@ def test_create_lambda_function_s3(self, session): ) arn = z.update_lambda_function( - bucket=bucket_name, s3_key=zip_path, function_name="test_lmbda_function55", + bucket=bucket_name, + s3_key=zip_path, + function_name="test_lmbda_function55", ) @placebo_session @@ -223,7 +227,10 @@ def test_handler(self, session): event = { "body": {}, "headers": {}, - "params": {"parameter_1": "asdf1", "parameter_2": "asdf2",}, + "params": { + "parameter_1": "asdf1", + "parameter_2": "asdf2", + }, "method": "GET", "query": {}, } diff --git a/zappa/cli.py b/zappa/cli.py index 6ba6dd5fe..44f462753 100755 --- a/zappa/cli.py +++ b/zappa/cli.py @@ -678,7 +678,9 @@ def dispatch_command(self, command, stage): command = command_tail[0] # ex: zappa manage dev showmigrations admin self.invoke( - command, command="manage", no_color=self.vargs["no_color"], + command, + command="manage", + no_color=self.vargs["no_color"], ) elif command == "tail": # pragma: no cover @@ -1499,7 +1501,9 @@ def unschedule(self): print("Unscheduling..") self.zappa.unschedule_events( - lambda_name=self.lambda_name, lambda_arn=function_arn, events=events, + lambda_name=self.lambda_name, + lambda_arn=function_arn, + events=events, ) # Remove async task SNS @@ -1528,7 +1532,9 @@ def invoke(self, function_name, raw_python=False, command=None, no_color=False): import json as json response = self.zappa.invoke_lambda_function( - self.lambda_name, json.dumps(command), invocation_type="RequestResponse", + self.lambda_name, + json.dumps(command), + invocation_type="RequestResponse", ) if "LogResult" in response: @@ -2912,8 +2918,10 @@ def get_zappa_settings_string(self): # If slim handler, path to project zip if self.stage_config.get("slim_handler", False): - settings_s += "ARCHIVE_PATH='s3://{0!s}/{1!s}_{2!s}_current_project.tar.gz'\n".format( - self.s3_bucket_name, self.api_stage, self.project_name + settings_s += ( + "ARCHIVE_PATH='s3://{0!s}/{1!s}_{2!s}_current_project.tar.gz'\n".format( + self.s3_bucket_name, self.api_stage, self.project_name + ) ) # since includes are for slim handler add the setting here by joining arbitrary list from zappa_settings file diff --git a/zappa/core.py b/zappa/core.py index d4737727e..78fc5ff5b 100644 --- a/zappa/core.py +++ b/zappa/core.py @@ -1051,7 +1051,9 @@ def upload_to_s3(self, source_path, bucket_name, disable_progress=False): # it's actually "US Standard", or something. # More here: https://github.com/boto/boto3/issues/125 if self.aws_region == "us-east-1": - self.s3_client.create_bucket(Bucket=bucket_name,) + self.s3_client.create_bucket( + Bucket=bucket_name, + ) else: self.s3_client.create_bucket( Bucket=bucket_name, @@ -1241,7 +1243,8 @@ def create_lambda_function( if concurrency is not None: self.lambda_client.put_function_concurrency( - FunctionName=resource_arn, ReservedConcurrentExecutions=concurrency, + FunctionName=resource_arn, + ReservedConcurrentExecutions=concurrency, ) return resource_arn @@ -1284,7 +1287,8 @@ def update_lambda_function( # https://github.com/Miserlou/Zappa/issues/1823 try: response = self.lambda_client.get_alias( - FunctionName=function_name, Name=ALB_LAMBDA_ALIAS, + FunctionName=function_name, + Name=ALB_LAMBDA_ALIAS, ) alias_exists = True except botocore.exceptions.ClientError as e: # pragma: no cover @@ -1301,7 +1305,8 @@ def update_lambda_function( if concurrency is not None: self.lambda_client.put_function_concurrency( - FunctionName=function_name, ReservedConcurrentExecutions=concurrency, + FunctionName=function_name, + ReservedConcurrentExecutions=concurrency, ) else: self.lambda_client.delete_function_concurrency(FunctionName=function_name) @@ -1389,7 +1394,11 @@ def update_lambda_configuration( if lambda_aws_config["PackageType"] != "Image": kwargs.update( - {"Handler": handler, "Runtime": runtime, "Layers": layers,} + { + "Handler": handler, + "Runtime": runtime, + "Layers": layers, + } ) response = self.lambda_client.update_function_configuration(**kwargs) @@ -1486,7 +1495,7 @@ def is_lambda_function_ready(self, function_name): def wait_until_lambda_function_is_ready(self, function_name): """ - Continuously check if a lambda function is active. + Continuously check if a lambda function is active. For functions deployed with a docker image instead of a ZIP package, the function can take a few seconds longer to be created or update, so we must wait before running any status @@ -1530,7 +1539,9 @@ def delete_lambda_function(self, function_name): """ print("Deleting Lambda function..") - return self.lambda_client.delete_function(FunctionName=function_name,) + return self.lambda_client.delete_function( + FunctionName=function_name, + ) ## # Application load balancer @@ -1652,7 +1663,12 @@ def deploy_lambda_alb(self, lambda_arn, lambda_name, alb_vpc_config, timeout): kwargs = dict( # TODO: Listeners support custom ssl certificates (Certificates). For now we leave this default. Certificates=[{"CertificateArn": alb_vpc_config["CertificateArn"]}], - DefaultActions=[{"Type": "forward", "TargetGroupArn": target_group_arn,}], + DefaultActions=[ + { + "Type": "forward", + "TargetGroupArn": target_group_arn, + } + ], LoadBalancerArn=load_balancer_arn, Protocol="HTTPS", # TODO: Add option for custom ports @@ -2147,7 +2163,12 @@ def remove_api_compression(self, api_id): """ self.apigateway_client.update_rest_api( restApiId=api_id, - patchOperations=[{"op": "replace", "path": "/minimumCompressionSize",}], + patchOperations=[ + { + "op": "replace", + "path": "/minimumCompressionSize", + } + ], ) def get_api_keys(self, api_id, stage_name): @@ -3333,7 +3354,9 @@ def create_async_dynamodb_table(self, table_name, read_capacity, write_capacity) dynamodb_table = self.dynamodb_client.create_table( AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], TableName=table_name, - KeySchema=[{"AttributeName": "id", "KeyType": "HASH"},], + KeySchema=[ + {"AttributeName": "id", "KeyType": "HASH"}, + ], ProvisionedThroughput={ "ReadCapacityUnits": read_capacity, "WriteCapacityUnits": write_capacity, diff --git a/zappa/letsencrypt.py b/zappa/letsencrypt.py index da22b2d57..9ecd63f02 100755 --- a/zappa/letsencrypt.py +++ b/zappa/letsencrypt.py @@ -41,7 +41,11 @@ def get_cert_and_update_domain( - zappa_instance, lambda_name, api_stage, domain=None, manual=False, + zappa_instance, + lambda_name, + api_stage, + domain=None, + manual=False, ): """ Main cert installer path. @@ -260,7 +264,10 @@ def get_cert(zappa_instance, log=LOGGER, CA=DEFAULT_CA): # get new challenge code, result = _send_signed_request( CA + "/acme/new-authz", - {"resource": "new-authz", "identifier": {"type": "dns", "value": domain},}, + { + "resource": "new-authz", + "identifier": {"type": "dns", "value": domain}, + }, ) if code != 201: raise ValueError( @@ -357,7 +364,11 @@ def sign_certificate(): devnull = open(os.devnull, "wb") csr_der = subprocess.check_output(cmd, stderr=devnull) code, result = _send_signed_request( - DEFAULT_CA + "/acme/new-cert", {"resource": "new-cert", "csr": _b64(csr_der),}, + DEFAULT_CA + "/acme/new-cert", + { + "resource": "new-cert", + "csr": _b64(csr_der), + }, ) if code != 201: raise ValueError("Error signing certificate: {0} {1}".format(code, result)) @@ -370,8 +381,10 @@ def encode_certificate(result): """ Encode cert bytes to PEM encoded cert file. """ - cert_body = """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( - "\n".join(textwrap.wrap(base64.b64encode(result).decode("utf8"), 64)) + cert_body = ( + """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( + "\n".join(textwrap.wrap(base64.b64encode(result).decode("utf8"), 64)) + ) ) signed_crt = open("{}/signed.crt".format(gettempdir()), "w") signed_crt.write(cert_body)