diff --git a/tests/test_awslambda/test_awslambda_cloudformation.py b/tests/test_awslambda/test_awslambda_cloudformation.py index 6d998bfd4..1924d2c27 100644 --- a/tests/test_awslambda/test_awslambda_cloudformation.py +++ b/tests/test_awslambda/test_awslambda_cloudformation.py @@ -5,10 +5,15 @@ import zipfile from botocore.exceptions import ClientError from moto import mock_cloudformation, mock_iam, mock_lambda, mock_s3, mock_sqs import pytest +import re from string import Template from uuid import uuid4 +def random_stack_name(): + return str(uuid4())[0:6] + + def _process_lambda(func_str): zip_output = io.BytesIO() zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) @@ -74,6 +79,7 @@ def test_lambda_can_be_updated_by_cloudformation(): cf = boto3.client("cloudformation", region_name="us-east-1") lmbda = boto3.client("lambda", region_name="us-east-1") body2, stack = create_stack(cf, s3) + stack_name = re.search(":stack/(.+)/", stack["StackId"]).group(1) created_fn_name = get_created_function_name(cf, stack) # Verify function has been created created_fn = lmbda.get_function(FunctionName=created_fn_name) @@ -83,7 +89,7 @@ def test_lambda_can_be_updated_by_cloudformation(): created_fn["Configuration"]["Runtime"].should.equal("python3.7") created_fn["Code"]["Location"].should.match("/test1.zip") # Update CF stack - cf.update_stack(StackName="teststack", TemplateBody=body2) + cf.update_stack(StackName=stack_name, TemplateBody=body2) updated_fn_name = get_created_function_name(cf, stack) # Verify function has been updated updated_fn = lmbda.get_function(FunctionName=updated_fn_name) @@ -124,7 +130,7 @@ def test_event_source_mapping_create_from_cloudformation_json(): cf = boto3.client("cloudformation", region_name="us-east-1") lmbda = boto3.client("lambda", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") + queue = sqs.create_queue(QueueName=str(uuid4())[0:6]) # Creates lambda _, lambda_stack = create_stack(cf, s3) @@ -143,7 +149,7 @@ def test_event_source_mapping_create_from_cloudformation_json(): } ) - cf.create_stack(StackName="test-event-source", TemplateBody=template) + cf.create_stack(StackName=random_stack_name(), TemplateBody=template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) event_sources["EventSourceMappings"].should.have.length_of(1) @@ -162,7 +168,7 @@ def test_event_source_mapping_delete_stack(): cf = boto3.client("cloudformation", region_name="us-east-1") lmbda = boto3.client("lambda", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") + queue = sqs.create_queue(QueueName=str(uuid4())[0:6]) # Creates lambda _, lambda_stack = create_stack(cf, s3) @@ -178,7 +184,7 @@ def test_event_source_mapping_delete_stack(): } ) - esm_stack = cf.create_stack(StackName="test-event-source", TemplateBody=template) + esm_stack = cf.create_stack(StackName=random_stack_name(), TemplateBody=template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) event_sources["EventSourceMappings"].should.have.length_of(1) @@ -199,7 +205,7 @@ def test_event_source_mapping_update_from_cloudformation_json(): cf = boto3.client("cloudformation", region_name="us-east-1") lmbda = boto3.client("lambda", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") + queue = sqs.create_queue(QueueName=str(uuid4())[0:6]) # Creates lambda _, lambda_stack = create_stack(cf, s3) @@ -218,7 +224,8 @@ def test_event_source_mapping_update_from_cloudformation_json(): } ) - cf.create_stack(StackName="test-event-source", TemplateBody=original_template) + stack_name = random_stack_name() + cf.create_stack(StackName=stack_name, TemplateBody=original_template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) original_esm = event_sources["EventSourceMappings"][0] @@ -236,7 +243,7 @@ def test_event_source_mapping_update_from_cloudformation_json(): } ) - cf.update_stack(StackName="test-event-source", TemplateBody=new_template) + cf.update_stack(StackName=stack_name, TemplateBody=new_template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) updated_esm = event_sources["EventSourceMappings"][0] @@ -254,7 +261,7 @@ def test_event_source_mapping_delete_from_cloudformation_json(): cf = boto3.client("cloudformation", region_name="us-east-1") lmbda = boto3.client("lambda", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") + queue = sqs.create_queue(QueueName=str(uuid4())[0:6]) # Creates lambda _, lambda_stack = create_stack(cf, s3) @@ -273,7 +280,8 @@ def test_event_source_mapping_delete_from_cloudformation_json(): } ) - cf.create_stack(StackName="test-event-source", TemplateBody=original_template) + stack_name = random_stack_name() + cf.create_stack(StackName=stack_name, TemplateBody=original_template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) original_esm = event_sources["EventSourceMappings"][0] @@ -291,7 +299,7 @@ def test_event_source_mapping_delete_from_cloudformation_json(): } ) - cf.update_stack(StackName="test-event-source", TemplateBody=new_template) + cf.update_stack(StackName=stack_name, TemplateBody=new_template) event_sources = lmbda.list_event_source_mappings(FunctionName=created_fn_name) event_sources["EventSourceMappings"].should.have.length_of(1) @@ -304,12 +312,13 @@ def test_event_source_mapping_delete_from_cloudformation_json(): def create_stack(cf, s3): bucket_name = str(uuid4()) + stack_name = random_stack_name() s3.create_bucket(Bucket=bucket_name) s3.put_object(Bucket=bucket_name, Key="test1.zip", Body=get_zip_file()) s3.put_object(Bucket=bucket_name, Key="test2.zip", Body=get_zip_file()) body1 = get_template(bucket_name, "1", "python3.7") body2 = get_template(bucket_name, "2", "python3.8") - stack = cf.create_stack(StackName="teststack", TemplateBody=body1) + stack = cf.create_stack(StackName=stack_name, TemplateBody=body1) return body2, stack @@ -334,10 +343,12 @@ def get_role_arn(): with mock_iam(): iam = boto3.client("iam", region_name="us-west-2") try: - return iam.get_role(RoleName="my-role")["Role"]["Arn"] - except ClientError: - return iam.create_role( + iam.create_role( RoleName="my-role", AssumeRolePolicyDocument="some policy", Path="/my-path/", - )["Role"]["Arn"] + ) + except ClientError: + pass # Will fail second/third time - difficult to execute once with parallel tests + + return iam.get_role(RoleName="my-role")["Role"]["Arn"] diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py index e135e0cb3..34fe9a98c 100644 --- a/tests/test_awslambda/test_lambda.py +++ b/tests/test_awslambda/test_lambda.py @@ -1,115 +1,25 @@ -from __future__ import unicode_literals - -import base64 -import uuid import botocore.client import boto3 import hashlib -import io import json -import time -import zipfile +import pytest import sure # noqa from freezegun import freeze_time -from moto import ( - mock_dynamodb2, - mock_lambda, - mock_iam, - mock_s3, - mock_ec2, - mock_sns, - mock_logs, - settings, - mock_sqs, +from moto import mock_lambda, mock_s3 +from moto.core.models import ACCOUNT_ID +from uuid import uuid4 +from .utilities import ( + get_role_name, + get_test_zip_file1, + get_test_zip_file2, + create_invalid_lambda, ) -from moto.sts.models import ACCOUNT_ID -from moto.core.exceptions import RESTError -import pytest -from botocore.exceptions import ClientError _lambda_region = "us-west-2" boto3.setup_default_session(region_name=_lambda_region) -def _process_lambda(func_str): - zip_output = io.BytesIO() - zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) - zip_file.writestr("lambda_function.py", func_str) - zip_file.close() - zip_output.seek(0) - return zip_output.read() - - -def get_test_zip_file1(): - pfunc = """ -def lambda_handler(event, context): - print("custom log event") - return event -""" - return _process_lambda(pfunc) - - -def get_test_zip_file2(): - func_str = """ -import boto3 - -def lambda_handler(event, context): - ec2 = boto3.resource('ec2', region_name='us-west-2', endpoint_url='http://{base_url}') - - volume_id = event.get('volume_id') - vol = ec2.Volume(volume_id) - - return {{'id': vol.id, 'state': vol.state, 'size': vol.size}} -""".format( - base_url="motoserver:5000" - if settings.TEST_SERVER_MODE - else "ec2.us-west-2.amazonaws.com" - ) - return _process_lambda(func_str) - - -def get_test_zip_file3(): - pfunc = """ -def lambda_handler(event, context): - print("Nr_of_records("+str(len(event['Records']))+")") - print("get_test_zip_file3 success") - return event -""" - return _process_lambda(pfunc) - - -def get_test_zip_file_error(): - pfunc = """ -def lambda_handler(event, context): - raise Exception('I failed!') -""" - return _process_lambda(pfunc) - - -def get_zip_with_multiple_files(): - pfunc = """ -from utilities import util_function -def lambda_handler(event, context): - x = util_function() - event["msg"] = event["msg"] + x - return event -""" - ufunc = """ -def util_function(): - return "stuff" -""" - zip_output = io.BytesIO() - zip_file = zipfile.ZipFile(zip_output, "a", zipfile.ZIP_DEFLATED) - zip_file.writestr("lambda_function.py", pfunc) - zip_file.close() - zip_file = zipfile.ZipFile(zip_output, "a", zipfile.ZIP_DEFLATED) - zip_file.writestr("utilities.py", ufunc) - zip_file.close() - zip_output.seek(0) - return zip_output.read() - - @pytest.mark.parametrize("region", ["us-west-2", "cn-northwest-1"]) @mock_lambda def test_lambda_regions(region): @@ -121,9 +31,10 @@ def test_lambda_regions(region): @mock_lambda def test_list_functions(): conn = boto3.client("lambda", _lambda_region) - conn.list_functions()["Functions"].should.have.length_of(0) - - function_name = "testFunction" + function_name = str(uuid4())[0:6] + initial_list = conn.list_functions()["Functions"] + initial_names = [f["FunctionName"] for f in initial_list] + initial_names.shouldnt.contain(function_name) conn.create_function( FunctionName=function_name, @@ -132,15 +43,20 @@ def test_list_functions(): Handler="lambda_function.lambda_handler", Code={"ZipFile": get_test_zip_file1()}, ) - conn.list_functions()["Functions"].should.have.length_of(1) + names = [f["FunctionName"] for f in conn.list_functions()["Functions"]] + names.should.contain(function_name) + conn.publish_version(FunctionName=function_name, Description="v2") - conn.list_functions()["Functions"].should.have.length_of(1) + func_list = conn.list_functions()["Functions"] + our_functions = [f for f in func_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(1) # FunctionVersion=ALL means we should get a list of all versions - result = conn.list_functions(FunctionVersion="ALL")["Functions"] - result.should.have.length_of(2) + full_list = conn.list_functions(FunctionVersion="ALL")["Functions"] + our_functions = [f for f in full_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(2) - v1 = [f for f in result if f["Version"] == "1"][0] + v1 = [f for f in our_functions if f["Version"] == "1"][0] v1["Description"].should.equal("v2") v1["FunctionArn"].should.equal( "arn:aws:lambda:{}:{}:function:{}:1".format( @@ -148,7 +64,7 @@ def test_list_functions(): ) ) - latest = [f for f in result if f["Version"] == "$LATEST"][0] + latest = [f for f in our_functions if f["Version"] == "$LATEST"][0] latest["Description"].should.equal("") latest["FunctionArn"].should.equal( "arn:aws:lambda:{}:{}:function:{}:$LATEST".format( @@ -157,273 +73,13 @@ def test_list_functions(): ) -@pytest.mark.network -@mock_lambda -def test_invoke_function_that_throws_error(): - conn = boto3.client("lambda", _lambda_region) - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file_error()}, - ) - - failure_response = conn.invoke( - FunctionName="testFunction", Payload=json.dumps({}), LogType="Tail" - ) - - failure_response.should.have.key("FunctionError").being.equal("Handled") - - payload = failure_response["Payload"].read().decode("utf-8") - payload = json.loads(payload) - payload["errorType"].should.equal("Exception") - payload["errorMessage"].should.equal("I failed!") - payload.should.have.key("stackTrace") - - logs = base64.b64decode(failure_response["LogResult"]).decode("utf-8") - logs.should.contain("START RequestId:") - logs.should.contain("I failed!: Exception") - logs.should.contain("Traceback (most recent call last):") - logs.should.contain("END RequestId:") - - -@pytest.mark.network -@pytest.mark.parametrize("invocation_type", [None, "RequestResponse"]) -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_lambda -def test_invoke_requestresponse_function(invocation_type, key): - conn = boto3.client("lambda", _lambda_region) - fxn = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = fxn[key] - - # Only add invocation-type keyword-argument when provided, otherwise the request - # fails to be validated - kw = {} - if invocation_type: - kw["InvocationType"] = invocation_type - - in_data = {"msg": "So long and thanks for all the fish"} - success_result = conn.invoke( - FunctionName=name_or_arn, Payload=json.dumps(in_data), LogType="Tail", **kw - ) - - if "FunctionError" in success_result: - assert False, success_result["Payload"].read().decode("utf-8") - - success_result["StatusCode"].should.equal(200) - success_result["ResponseMetadata"]["HTTPHeaders"]["content-type"].should.equal( - "application/json" - ) - logs = base64.b64decode(success_result["LogResult"]).decode("utf-8") - - logs.should.contain("START RequestId:") - logs.should.contain("custom log event") - logs.should.contain("END RequestId:") - - payload = success_result["Payload"].read().decode("utf-8") - json.loads(payload).should.equal(in_data) - - # Logs should not be returned by default, only when the LogType-param is supplied - success_result = conn.invoke( - FunctionName=name_or_arn, Payload=json.dumps(in_data), **kw - ) - - success_result["StatusCode"].should.equal(200) - success_result["ResponseMetadata"]["HTTPHeaders"]["content-type"].should.equal( - "application/json" - ) - assert "LogResult" not in success_result - - -@pytest.mark.network -@mock_lambda -def test_invoke_event_function(): - conn = boto3.client("lambda", _lambda_region) - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - conn.invoke.when.called_with( - FunctionName="notAFunction", InvocationType="Event", Payload="{}" - ).should.throw(botocore.client.ClientError) - - in_data = {"msg": "So long and thanks for all the fish"} - success_result = conn.invoke( - FunctionName="testFunction", InvocationType="Event", Payload=json.dumps(in_data) - ) - success_result["StatusCode"].should.equal(202) - json.loads(success_result["Payload"].read().decode("utf-8")).should.equal(in_data) - - -@pytest.mark.network -@mock_lambda -def test_invoke_function_with_multiple_files_in_zip(): - conn = boto3.client("lambda", _lambda_region) - conn.create_function( - FunctionName="testFunction", - Runtime="python3.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_zip_with_multiple_files()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - in_data = {"msg": "So long and thanks for: "} - success_result = conn.invoke( - FunctionName="testFunction", InvocationType="Event", Payload=json.dumps(in_data) - ) - json.loads(success_result["Payload"].read().decode("utf-8")).should.equal( - {"msg": "So long and thanks for: stuff"} - ) - - -@pytest.mark.network -@mock_lambda -def test_invoke_dryrun_function(): - conn = boto3.client("lambda", _lambda_region) - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file1(),}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - conn.invoke.when.called_with( - FunctionName="notAFunction", InvocationType="Event", Payload="{}" - ).should.throw(botocore.client.ClientError) - - in_data = {"msg": "So long and thanks for all the fish"} - success_result = conn.invoke( - FunctionName="testFunction", - InvocationType="DryRun", - Payload=json.dumps(in_data), - ) - success_result["StatusCode"].should.equal(204) - - -if settings.TEST_SERVER_MODE: - - @mock_ec2 - @mock_lambda - def test_invoke_function_get_ec2_volume(): - conn = boto3.resource("ec2", _lambda_region) - vol = conn.create_volume(Size=99, AvailabilityZone=_lambda_region) - vol = conn.Volume(vol.id) - - conn = boto3.client("lambda", _lambda_region) - conn.create_function( - FunctionName="testFunction", - Runtime="python3.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file2()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - in_data = {"volume_id": vol.id} - result = conn.invoke( - FunctionName="testFunction", - InvocationType="RequestResponse", - Payload=json.dumps(in_data), - ) - result["StatusCode"].should.equal(200) - actual_payload = json.loads(result["Payload"].read().decode("utf-8")) - expected_payload = {"id": vol.id, "state": vol.state, "size": vol.size} - actual_payload.should.equal(expected_payload) - - -@pytest.mark.network -@mock_logs -@mock_sns -@mock_ec2 -@mock_lambda -def test_invoke_function_from_sns(): - logs_conn = boto3.client("logs", region_name=_lambda_region) - sns_conn = boto3.client("sns", region_name=_lambda_region) - sns_conn.create_topic(Name="some-topic") - topics_json = sns_conn.list_topics() - topics = topics_json["Topics"] - topic_arn = topics[0]["TopicArn"] - - conn = boto3.client("lambda", _lambda_region) - result = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - sns_conn.subscribe( - TopicArn=topic_arn, Protocol="lambda", Endpoint=result["FunctionArn"] - ) - - result = sns_conn.publish(TopicArn=topic_arn, Message=json.dumps({})) - - start = time.time() - events = [] - while (time.time() - start) < 10: - result = logs_conn.describe_log_streams(logGroupName="/aws/lambda/testFunction") - log_streams = result.get("logStreams") - if not log_streams: - time.sleep(1) - continue - - assert len(log_streams) == 1 - result = logs_conn.get_log_events( - logGroupName="/aws/lambda/testFunction", - logStreamName=log_streams[0]["logStreamName"], - ) - events = result.get("events") - for event in events: - if event["message"] == "get_test_zip_file3 success": - return - - time.sleep(1) - - assert False, "Expected message not found in logs:" + str(events) - - @mock_lambda def test_create_based_on_s3_with_missing_bucket(): conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function.when.called_with( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", @@ -440,22 +96,24 @@ def test_create_based_on_s3_with_missing_bucket(): @mock_s3 @freeze_time("2015-01-01 00:00:00") def test_create_function_from_aws_bucket(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] result = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -469,9 +127,9 @@ def test_create_function_from_aws_bucket(): result.pop("LastModified") result.should.equal( { - "FunctionName": "testFunction", - "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format( - _lambda_region, ACCOUNT_ID + "FunctionName": function_name, + "FunctionArn": "arn:aws:lambda:{}:{}:function:{}".format( + _lambda_region, ACCOUNT_ID, function_name ), "Runtime": "python2.7", "Role": result["Role"], @@ -499,8 +157,9 @@ def test_create_function_from_aws_bucket(): def test_create_function_from_zipfile(): conn = boto3.client("lambda", _lambda_region) zip_content = get_test_zip_file1() + function_name = str(uuid4())[0:6] result = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", @@ -518,9 +177,9 @@ def test_create_function_from_zipfile(): result.should.equal( { - "FunctionName": "testFunction", - "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format( - _lambda_region, ACCOUNT_ID + "FunctionName": function_name, + "FunctionArn": "arn:aws:lambda:{}:{}:function:{}".format( + _lambda_region, ACCOUNT_ID, function_name ), "Runtime": "python2.7", "Role": result["Role"], @@ -543,22 +202,24 @@ def test_create_function_from_zipfile(): @mock_s3 @freeze_time("2015-01-01 00:00:00") def test_get_function(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file1() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -566,7 +227,7 @@ def test_get_function(): Environment={"Variables": {"test_variable": "test_value"}}, ) - result = conn.get_function(FunctionName="testFunction") + result = conn.get_function(FunctionName=function_name) # this is hard to match against, so remove it result["ResponseMetadata"].pop("HTTPHeaders", None) # Botocore inserts retry attempts not seen in Python27 @@ -584,7 +245,7 @@ def test_get_function(): result["Configuration"]["CodeSize"].should.equal(len(zip_content)) result["Configuration"]["Description"].should.equal("test lambda function") result["Configuration"].should.contain("FunctionArn") - result["Configuration"]["FunctionName"].should.equal("testFunction") + result["Configuration"]["FunctionName"].should.equal(function_name) result["Configuration"]["Handler"].should.equal("lambda_function.lambda_handler") result["Configuration"]["MemorySize"].should.equal(128) result["Configuration"]["Role"].should.equal(get_role_name()) @@ -599,10 +260,12 @@ def test_get_function(): ) # Test get function with qualifier - result = conn.get_function(FunctionName="testFunction", Qualifier="$LATEST") + result = conn.get_function(FunctionName=function_name, Qualifier="$LATEST") result["Configuration"]["Version"].should.equal("$LATEST") result["Configuration"]["FunctionArn"].should.equal( - "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID) + "arn:aws:lambda:us-west-2:{}:function:{}:$LATEST".format( + ACCOUNT_ID, function_name + ) ) # Test get function when can't find function name @@ -615,22 +278,24 @@ def test_get_function(): @mock_s3 @freeze_time("2015-01-01 00:00:00") def test_get_function_configuration(key): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file1() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] fxn = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -645,7 +310,7 @@ def test_get_function_configuration(key): result["CodeSize"].should.equal(len(zip_content)) result["Description"].should.equal("test lambda function") result.should.contain("FunctionArn") - result["FunctionName"].should.equal("testFunction") + result["FunctionName"].should.equal(function_name) result["Handler"].should.equal("lambda_function.lambda_handler") result["MemorySize"].should.equal(128) result["Role"].should.equal(get_role_name()) @@ -663,8 +328,8 @@ def test_get_function_configuration(key): ) result["Version"].should.equal("$LATEST") result["FunctionArn"].should.equal( - "arn:aws:lambda:{}:{}:function:testFunction:$LATEST".format( - _lambda_region, ACCOUNT_ID + "arn:aws:lambda:{}:{}:function:{}:$LATEST".format( + _lambda_region, ACCOUNT_ID, function_name ) ) @@ -676,7 +341,7 @@ def test_get_function_configuration(key): @mock_lambda @mock_s3 def test_get_function_by_arn(): - bucket_name = "test-bucket" + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", "us-east-1") s3_conn.create_bucket( Bucket=bucket_name, @@ -686,9 +351,10 @@ def test_get_function_by_arn(): zip_content = get_test_zip_file2() s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", "us-east-1") + function_name = str(uuid4())[0:6] fnc = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", @@ -700,35 +366,37 @@ def test_get_function_by_arn(): ) result = conn.get_function(FunctionName=fnc["FunctionArn"]) - result["Configuration"]["FunctionName"].should.equal("testFunction") + result["Configuration"]["FunctionName"].should.equal(function_name) @mock_lambda @mock_s3 def test_delete_function(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, Publish=True, ) - success_result = conn.delete_function(FunctionName="testFunction") + success_result = conn.delete_function(FunctionName=function_name) # this is hard to match against, so remove it success_result["ResponseMetadata"].pop("HTTPHeaders", None) # Botocore inserts retry attempts not seen in Python27 @@ -736,14 +404,15 @@ def test_delete_function(): success_result.should.equal({"ResponseMetadata": {"HTTPStatusCode": 204}}) - function_list = conn.list_functions() - function_list["Functions"].should.have.length_of(0) + func_list = conn.list_functions()["Functions"] + our_functions = [f for f in func_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(0) @mock_lambda @mock_s3 def test_delete_function_by_arn(): - bucket_name = "test-bucket" + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", "us-east-1") s3_conn.create_bucket( Bucket=bucket_name, @@ -753,9 +422,10 @@ def test_delete_function_by_arn(): zip_content = get_test_zip_file2() s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", "us-east-1") + function_name = str(uuid4())[0:6] fnc = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", @@ -767,8 +437,10 @@ def test_delete_function_by_arn(): ) conn.delete_function(FunctionName=fnc["FunctionArn"]) - function_list = conn.list_functions() - function_list["Functions"].should.have.length_of(0) + + func_list = conn.list_functions()["Functions"] + our_functions = [f for f in func_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(0) @mock_lambda @@ -782,49 +454,52 @@ def test_delete_unknown_function(): @mock_lambda @mock_s3 def test_publish(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, Publish=False, ) - function_list = conn.list_functions(FunctionVersion="ALL") - function_list["Functions"].should.have.length_of(1) - latest_arn = function_list["Functions"][0]["FunctionArn"] + function_list = conn.list_functions(FunctionVersion="ALL")["Functions"] + our_functions = [f for f in function_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(1) + latest_arn = our_functions[0]["FunctionArn"] - res = conn.publish_version(FunctionName="testFunction") + res = conn.publish_version(FunctionName=function_name) assert res["ResponseMetadata"]["HTTPStatusCode"] == 201 - function_list = conn.list_functions(FunctionVersion="ALL") - function_list["Functions"].should.have.length_of(2) + function_list = conn.list_functions(FunctionVersion="ALL")["Functions"] + our_functions = [f for f in function_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(2) # #SetComprehension ;-) - published_arn = list( - {f["FunctionArn"] for f in function_list["Functions"]} - {latest_arn} - )[0] - published_arn.should.contain("testFunction:1") + published_arn = list({f["FunctionArn"] for f in our_functions} - {latest_arn})[0] + published_arn.should.contain("{}:1".format(function_name)) - conn.delete_function(FunctionName="testFunction", Qualifier="1") + conn.delete_function(FunctionName=function_name, Qualifier="1") - function_list = conn.list_functions() - function_list["Functions"].should.have.length_of(1) - function_list["Functions"][0]["FunctionArn"].should.contain("testFunction") + function_list = conn.list_functions()["Functions"] + our_functions = [f for f in function_list if f["FunctionName"] == function_name] + our_functions.should.have.length_of(1) + our_functions[0]["FunctionArn"].should.contain(function_name) @mock_lambda @@ -835,25 +510,29 @@ def test_list_create_list_get_delete_list(): test `list -> create -> list -> get -> delete -> list` integration """ + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] - conn.list_functions()["Functions"].should.have.length_of(0) + initial_list = conn.list_functions()["Functions"] + initial_names = [f["FunctionName"] for f in initial_list] + initial_names.shouldnt.contain(function_name) - function_name = "testFunction" + function_name = function_name conn.create_function( FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -884,16 +563,22 @@ def test_list_create_list_get_delete_list(): "ResponseMetadata": {"HTTPStatusCode": 200}, } functions = conn.list_functions()["Functions"] - functions.should.have.length_of(1) - functions[0]["FunctionArn"].should.equal( + func_names = [f["FunctionName"] for f in functions] + func_names.should.contain(function_name) + + func_arn = [ + f["FunctionArn"] for f in functions if f["FunctionName"] == function_name + ][0] + func_arn.should.equal( "arn:aws:lambda:{}:{}:function:{}".format( _lambda_region, ACCOUNT_ID, function_name ) ) functions = conn.list_functions(FunctionVersion="ALL")["Functions"] - functions.should.have.length_of(2) + our_functions = [f for f in functions if f["FunctionName"] == function_name] + our_functions.should.have.length_of(2) - latest = [f for f in functions if f["Version"] == "$LATEST"][0] + latest = [f for f in our_functions if f["Version"] == "$LATEST"][0] latest["FunctionArn"].should.equal( "arn:aws:lambda:{}:{}:function:{}:$LATEST".format( _lambda_region, ACCOUNT_ID, function_name @@ -903,7 +588,7 @@ def test_list_create_list_get_delete_list(): latest.pop("LastModified") latest.should.equal(expected_function_result["Configuration"]) - published = [f for f in functions if f["Version"] != "$LATEST"][0] + published = [f for f in our_functions if f["Version"] != "$LATEST"][0] published["Version"].should.equal("1") published["FunctionArn"].should.equal( "arn:aws:lambda:{}:{}:function:{}:1".format( @@ -927,43 +612,11 @@ def test_list_create_list_get_delete_list(): func["Configuration"].pop("FunctionArn") func.should.equal(expected_function_result) - conn.delete_function(FunctionName="testFunction") + conn.delete_function(FunctionName=function_name) - conn.list_functions()["Functions"].should.have.length_of(0) - - -@pytest.mark.network -@mock_lambda -def test_invoke_lambda_error(): - lambda_fx = """ -def lambda_handler(event, context): - raise Exception('failsauce') - """ - zip_output = io.BytesIO() - zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) - zip_file.writestr("lambda_function.py", lambda_fx) - zip_file.close() - zip_output.seek(0) - - client = boto3.client("lambda", region_name="us-east-1") - client.create_function( - FunctionName="test-lambda-fx", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - Code={"ZipFile": zip_output.read()}, - ) - - result = client.invoke( - FunctionName="test-lambda-fx", InvocationType="RequestResponse", LogType="Tail" - ) - - assert "FunctionError" in result - assert result["FunctionError"] == "Handled" + functions = conn.list_functions()["Functions"] + func_names = [f["FunctionName"] for f in functions] + func_names.shouldnt.contain(function_name) @mock_lambda @@ -972,22 +625,24 @@ def test_tags(): """ test list_tags -> tag_resource -> list_tags -> tag_resource -> list_tags -> untag_resource -> list_tags integration """ + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] function = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -1048,38 +703,14 @@ def test_tags_not_found(): ).should.throw(botocore.client.ClientError) -@pytest.mark.network -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_lambda -def test_invoke_async_function(key): - conn = boto3.client("lambda", _lambda_region) - fxn = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = fxn[key] - - success_result = conn.invoke_async( - FunctionName=name_or_arn, InvokeArgs=json.dumps({"test": "event"}) - ) - - success_result["Status"].should.equal(202) - - @mock_lambda @freeze_time("2015-01-01 00:00:00") def test_get_function_created_with_zipfile(): conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] zip_content = get_test_zip_file1() result = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.handler", @@ -1090,7 +721,7 @@ def test_get_function_created_with_zipfile(): Publish=True, ) - response = conn.get_function(FunctionName="testFunction") + response = conn.get_function(FunctionName=function_name) response["Configuration"].pop("LastModified") response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) @@ -1104,10 +735,10 @@ def test_get_function_created_with_zipfile(): "CodeSha256": hashlib.sha256(zip_content).hexdigest(), "CodeSize": len(zip_content), "Description": "test lambda function", - "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format( - _lambda_region, ACCOUNT_ID + "FunctionArn": "arn:aws:lambda:{}:{}:function:{}".format( + _lambda_region, ACCOUNT_ID, function_name ), - "FunctionName": "testFunction", + "FunctionName": function_name, "Handler": "lambda_function.handler", "MemorySize": 128, "Role": get_role_name(), @@ -1129,8 +760,9 @@ def test_add_function_permission(key): """ conn = boto3.client("lambda", _lambda_region) zip_content = get_test_zip_file1() + function_name = str(uuid4())[0:6] f = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=(get_role_name()), Handler="lambda_function.handler", @@ -1162,8 +794,9 @@ def test_add_function_permission(key): def test_get_function_policy(key): conn = boto3.client("lambda", _lambda_region) zip_content = get_test_zip_file1() + function_name = str(uuid4())[0:6] f = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.handler", @@ -1196,48 +829,52 @@ def test_get_function_policy(key): @mock_lambda @mock_s3 def test_list_versions_by_function(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, Publish=True, ) - res = conn.publish_version(FunctionName="testFunction") + res = conn.publish_version(FunctionName=function_name) assert res["ResponseMetadata"]["HTTPStatusCode"] == 201 - versions = conn.list_versions_by_function(FunctionName="testFunction") + versions = conn.list_versions_by_function(FunctionName=function_name) assert len(versions["Versions"]) == 3 assert versions["Versions"][0][ "FunctionArn" - ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID) + ] == "arn:aws:lambda:us-west-2:{}:function:{}:$LATEST".format( + ACCOUNT_ID, function_name + ) assert versions["Versions"][1][ "FunctionArn" - ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:1".format(ACCOUNT_ID) + ] == "arn:aws:lambda:us-west-2:{}:function:{}:1".format(ACCOUNT_ID, function_name) assert versions["Versions"][2][ "FunctionArn" - ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:2".format(ACCOUNT_ID) + ] == "arn:aws:lambda:us-west-2:{}:function:{}:2".format(ACCOUNT_ID, function_name) conn.create_function( FunctionName="testFunction_2", Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -1255,22 +892,24 @@ def test_list_versions_by_function(): @mock_lambda @mock_s3 def test_create_function_with_already_exists(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -1278,456 +917,52 @@ def test_create_function_with_already_exists(): ) response = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, Publish=True, ) - assert response["FunctionName"] == "testFunction" + assert response["FunctionName"] == function_name @mock_lambda @mock_s3 def test_list_versions_by_function_for_nonexistent_function(): conn = boto3.client("lambda", _lambda_region) - versions = conn.list_versions_by_function(FunctionName="testFunction") + function_name = str(uuid4())[0:6] + versions = conn.list_versions_by_function(FunctionName=function_name) assert len(versions["Versions"]) == 0 -@mock_logs -@mock_lambda -@mock_sqs -def test_create_event_source_mapping(): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] - ) - - assert response["EventSourceArn"] == queue.attributes["QueueArn"] - assert response["FunctionArn"] == func["FunctionArn"] - assert response["State"] == "Enabled" - - -@pytest.mark.network -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_logs -@mock_lambda -@mock_sqs -def test_invoke_function_from_sqs(key): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = func[key] - - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=name_or_arn - ) - - assert response["EventSourceArn"] == queue.attributes["QueueArn"] - assert response["State"] == "Enabled" - - sqs_client = boto3.client("sqs", region_name="us-east-1") - sqs_client.send_message(QueueUrl=queue.url, MessageBody="test") - - expected_msg = "get_test_zip_file3 success" - log_group = "/aws/lambda/testFunction" - msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) - - assert msg_showed_up, ( - expected_msg - + " was not found after sending an SQS message. All logs: " - + str(all_logs) - ) - - -@pytest.mark.network -@mock_logs -@mock_lambda -@mock_dynamodb2 -def test_invoke_function_from_dynamodb_put(): - dynamodb = boto3.client("dynamodb", region_name="us-east-1") - table_name = "table_with_stream" - table = dynamodb.create_table( - TableName=table_name, - KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], - AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], - StreamSpecification={ - "StreamEnabled": True, - "StreamViewType": "NEW_AND_OLD_IMAGES", - }, - ) - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function executed after a DynamoDB table is updated", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - response = conn.create_event_source_mapping( - EventSourceArn=table["TableDescription"]["LatestStreamArn"], - FunctionName=func["FunctionArn"], - ) - - assert response["EventSourceArn"] == table["TableDescription"]["LatestStreamArn"] - assert response["State"] == "Enabled" - - dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}}) - - expected_msg = "get_test_zip_file3 success" - log_group = "/aws/lambda/testFunction" - msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) - - assert msg_showed_up, ( - expected_msg + " was not found after a DDB insert. All logs: " + str(all_logs) - ) - - -@pytest.mark.network -@mock_logs -@mock_lambda -@mock_dynamodb2 -def test_invoke_function_from_dynamodb_update(): - dynamodb = boto3.client("dynamodb", region_name="us-east-1") - table_name = "table_with_stream" - table = dynamodb.create_table( - TableName=table_name, - KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], - AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], - StreamSpecification={ - "StreamEnabled": True, - "StreamViewType": "NEW_AND_OLD_IMAGES", - }, - ) - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function executed after a DynamoDB table is updated", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - conn.create_event_source_mapping( - EventSourceArn=table["TableDescription"]["LatestStreamArn"], - FunctionName=func["FunctionArn"], - ) - - dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}}) - log_group = "/aws/lambda/testFunction" - expected_msg = "get_test_zip_file3 success" - msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) - assert "Nr_of_records(1)" in all_logs, "Only one item should be inserted" - - dynamodb.update_item( - TableName=table_name, - Key={"id": {"S": "item 1"}}, - UpdateExpression="set #attr = :val", - ExpressionAttributeNames={"#attr": "new_attr"}, - ExpressionAttributeValues={":val": {"S": "new_val"}}, - ) - msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) - - assert msg_showed_up, ( - expected_msg + " was not found after updating DDB. All logs: " + str(all_logs) - ) - assert "Nr_of_records(1)" in all_logs, "Only one item should be updated" - assert ( - "Nr_of_records(2)" not in all_logs - ), "The inserted item should not show up again" - - -def wait_for_log_msg(expected_msg, log_group): - logs_conn = boto3.client("logs", region_name="us-east-1") - received_messages = [] - start = time.time() - while (time.time() - start) < 30: - result = logs_conn.describe_log_streams(logGroupName=log_group) - log_streams = result.get("logStreams") - if not log_streams: - time.sleep(1) - continue - - for log_stream in log_streams: - result = logs_conn.get_log_events( - logGroupName=log_group, logStreamName=log_stream["logStreamName"], - ) - received_messages.extend( - [event["message"] for event in result.get("events")] - ) - if expected_msg in received_messages: - return True, received_messages - time.sleep(1) - return False, received_messages - - -@pytest.mark.network -@mock_logs -@mock_lambda -@mock_sqs -def test_invoke_function_from_sqs_exception(): - logs_conn = boto3.client("logs", region_name="us-east-1") - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file_error()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] - ) - - assert response["EventSourceArn"] == queue.attributes["QueueArn"] - assert response["State"] == "Enabled" - - entries = [] - for i in range(3): - body = {"uuid": str(uuid.uuid4()), "test": "test_{}".format(i)} - entry = {"Id": str(i), "MessageBody": json.dumps(body)} - entries.append(entry) - - queue.send_messages(Entries=entries) - - start = time.time() - while (time.time() - start) < 30: - result = logs_conn.describe_log_streams(logGroupName="/aws/lambda/testFunction") - log_streams = result.get("logStreams") - if not log_streams: - time.sleep(1) - continue - assert len(log_streams) >= 1 - - result = logs_conn.get_log_events( - logGroupName="/aws/lambda/testFunction", - logStreamName=log_streams[0]["logStreamName"], - ) - for event in result.get("events"): - if "I failed!" in event["message"]: - messages = queue.receive_messages(MaxNumberOfMessages=10) - # Verify messages are still visible and unprocessed - assert len(messages) == 3 - return - time.sleep(1) - - assert False, "Test Failed" - - -@mock_logs -@mock_lambda -@mock_sqs -def test_list_event_source_mappings(): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] - ) - mappings = conn.list_event_source_mappings(EventSourceArn="123") - assert len(mappings["EventSourceMappings"]) == 0 - - mappings = conn.list_event_source_mappings( - EventSourceArn=queue.attributes["QueueArn"] - ) - assert len(mappings["EventSourceMappings"]) == 1 - assert mappings["EventSourceMappings"][0]["UUID"] == response["UUID"] - assert mappings["EventSourceMappings"][0]["FunctionArn"] == func["FunctionArn"] - - -@mock_lambda -@mock_sqs -def test_get_event_source_mapping(): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] - ) - mapping = conn.get_event_source_mapping(UUID=response["UUID"]) - assert mapping["UUID"] == response["UUID"] - assert mapping["FunctionArn"] == func["FunctionArn"] - - conn.get_event_source_mapping.when.called_with(UUID="1").should.throw( - botocore.client.ClientError - ) - - -@mock_lambda -@mock_sqs -def test_update_event_source_mapping(): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func1 = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - func2 = conn.create_function( - FunctionName="testFunction2", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func1["FunctionArn"] - ) - assert response["FunctionArn"] == func1["FunctionArn"] - assert response["BatchSize"] == 10 - assert response["State"] == "Enabled" - - mapping = conn.update_event_source_mapping( - UUID=response["UUID"], Enabled=False, BatchSize=2, FunctionName="testFunction2" - ) - assert mapping["UUID"] == response["UUID"] - assert mapping["FunctionArn"] == func2["FunctionArn"] - assert mapping["State"] == "Disabled" - assert mapping["BatchSize"] == 2 - - -@mock_lambda -@mock_sqs -def test_delete_event_source_mapping(): - sqs = boto3.resource("sqs", region_name="us-east-1") - queue = sqs.create_queue(QueueName="test-sqs-queue1") - - conn = boto3.client("lambda", region_name="us-east-1") - func1 = conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"ZipFile": get_test_zip_file3()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - response = conn.create_event_source_mapping( - EventSourceArn=queue.attributes["QueueArn"], FunctionName=func1["FunctionArn"] - ) - assert response["FunctionArn"] == func1["FunctionArn"] - assert response["BatchSize"] == 10 - assert response["State"] == "Enabled" - - response = conn.delete_event_source_mapping(UUID=response["UUID"]) - - assert response["State"] == "Deleting" - conn.get_event_source_mapping.when.called_with(UUID=response["UUID"]).should.throw( - botocore.client.ClientError - ) - - @pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) @mock_lambda @mock_s3 def test_update_configuration(key): + bucket_name = str(uuid4()) + function_name = str(uuid4())[0:6] s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) fxn = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -1774,9 +1009,10 @@ def test_update_function_zip(key): conn = boto3.client("lambda", _lambda_region) zip_content_one = get_test_zip_file1() + function_name = str(uuid4())[0:6] fxn = conn.create_function( - FunctionName="testFunctionZip", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", @@ -1794,7 +1030,7 @@ def test_update_function_zip(key): FunctionName=name_or_arn, ZipFile=zip_content_two, Publish=True ) - response = conn.get_function(FunctionName="testFunctionZip", Qualifier="2") + response = conn.get_function(FunctionName=function_name, Qualifier="2") response["Configuration"].pop("LastModified") response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) @@ -1808,10 +1044,10 @@ def test_update_function_zip(key): "CodeSha256": hashlib.sha256(zip_content_two).hexdigest(), "CodeSize": len(zip_content_two), "Description": "test lambda function", - "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionZip:2".format( - _lambda_region, ACCOUNT_ID + "FunctionArn": "arn:aws:lambda:{}:{}:function:{}:2".format( + _lambda_region, ACCOUNT_ID, function_name ), - "FunctionName": "testFunctionZip", + "FunctionName": function_name, "Handler": "lambda_function.lambda_handler", "MemorySize": 128, "Role": fxn["Role"], @@ -1828,23 +1064,25 @@ def test_update_function_zip(key): @mock_lambda @mock_s3 def test_update_function_s3(): + bucket_name = str(uuid4()) s3_conn = boto3.client("s3", _lambda_region) s3_conn.create_bucket( - Bucket="test-bucket", + Bucket=bucket_name, CreateBucketConfiguration={"LocationConstraint": _lambda_region}, ) zip_content = get_test_zip_file1() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] fxn = conn.create_function( - FunctionName="testFunctionS3", + FunctionName=function_name, Runtime="python2.7", Role=get_role_name(), Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, Description="test lambda function", Timeout=3, MemorySize=128, @@ -1852,16 +1090,16 @@ def test_update_function_s3(): ) zip_content_two = get_test_zip_file2() - s3_conn.put_object(Bucket="test-bucket", Key="test2.zip", Body=zip_content_two) + s3_conn.put_object(Bucket=bucket_name, Key="test2.zip", Body=zip_content_two) fxn_updated = conn.update_function_code( - FunctionName="testFunctionS3", - S3Bucket="test-bucket", + FunctionName=function_name, + S3Bucket=bucket_name, S3Key="test2.zip", Publish=True, ) - response = conn.get_function(FunctionName="testFunctionS3", Qualifier="2") + response = conn.get_function(FunctionName=function_name, Qualifier="2") response["Configuration"].pop("LastModified") response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) @@ -1875,10 +1113,10 @@ def test_update_function_s3(): "CodeSha256": hashlib.sha256(zip_content_two).hexdigest(), "CodeSize": len(zip_content_two), "Description": "test lambda function", - "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionS3:2".format( - _lambda_region, ACCOUNT_ID + "FunctionArn": "arn:aws:lambda:{}:{}:function:{}:2".format( + _lambda_region, ACCOUNT_ID, function_name ), - "FunctionName": "testFunctionS3", + "FunctionName": function_name, "Handler": "lambda_function.lambda_handler", "MemorySize": 128, "Role": fxn["Role"], @@ -1923,8 +1161,9 @@ def test_create_function_with_unknown_arn(): def test_remove_function_permission(key): conn = boto3.client("lambda", _lambda_region) zip_content = get_test_zip_file1() + function_name = str(uuid4())[0:6] f = conn.create_function( - FunctionName="testFunction", + FunctionName=function_name, Runtime="python2.7", Role=(get_role_name()), Handler="lambda_function.handler", @@ -1954,223 +1193,3 @@ def test_remove_function_permission(key): policy = conn.get_policy(FunctionName=name_or_arn, Qualifier="2")["Policy"] policy = json.loads(policy) policy["Statement"].should.equal([]) - - -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_lambda -def test_put_function_concurrency(key): - expected_concurrency = 15 - function_name = "test" - - conn = boto3.client("lambda", _lambda_region) - f = conn.create_function( - FunctionName=function_name, - Runtime="python3.8", - Role=(get_role_name()), - Handler="lambda_function.handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = f[key] - result = conn.put_function_concurrency( - FunctionName=name_or_arn, ReservedConcurrentExecutions=expected_concurrency - ) - - result["ReservedConcurrentExecutions"].should.equal(expected_concurrency) - - -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_lambda -def test_delete_function_concurrency(key): - function_name = "test" - - conn = boto3.client("lambda", _lambda_region) - f = conn.create_function( - FunctionName=function_name, - Runtime="python3.8", - Role=(get_role_name()), - Handler="lambda_function.handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = f[key] - conn.put_function_concurrency( - FunctionName=name_or_arn, ReservedConcurrentExecutions=15 - ) - - conn.delete_function_concurrency(FunctionName=name_or_arn) - result = conn.get_function(FunctionName=function_name) - - result.doesnt.have.key("Concurrency") - - -@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) -@mock_lambda -def test_get_function_concurrency(key): - expected_concurrency = 15 - function_name = "test" - - conn = boto3.client("lambda", _lambda_region) - f = conn.create_function( - FunctionName=function_name, - Runtime="python3.8", - Role=(get_role_name()), - Handler="lambda_function.handler", - Code={"ZipFile": get_test_zip_file1()}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - name_or_arn = f[key] - conn.put_function_concurrency( - FunctionName=name_or_arn, ReservedConcurrentExecutions=expected_concurrency - ) - - result = conn.get_function_concurrency(FunctionName=name_or_arn) - - result["ReservedConcurrentExecutions"].should.equal(expected_concurrency) - - -@mock_lambda -@mock_s3 -@freeze_time("2015-01-01 00:00:00") -def test_get_lambda_layers(): - s3_conn = boto3.client("s3", _lambda_region) - s3_conn.create_bucket( - Bucket="test-bucket", - CreateBucketConfiguration={"LocationConstraint": _lambda_region}, - ) - - zip_content = get_test_zip_file1() - s3_conn.put_object(Bucket="test-bucket", Key="test.zip", Body=zip_content) - conn = boto3.client("lambda", _lambda_region) - - with pytest.raises((RESTError, ClientError)): - conn.publish_layer_version( - LayerName="testLayer", - Content={}, - CompatibleRuntimes=["python3.6"], - LicenseInfo="MIT", - ) - conn.publish_layer_version( - LayerName="testLayer", - Content={"ZipFile": get_test_zip_file1()}, - CompatibleRuntimes=["python3.6"], - LicenseInfo="MIT", - ) - conn.publish_layer_version( - LayerName="testLayer", - Content={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, - CompatibleRuntimes=["python3.6"], - LicenseInfo="MIT", - ) - - result = conn.list_layer_versions(LayerName="testLayer") - - for version in result["LayerVersions"]: - version.pop("CreatedDate") - result["LayerVersions"].sort(key=lambda x: x["Version"]) - expected_arn = "arn:aws:lambda:{0}:{1}:layer:testLayer:".format( - _lambda_region, ACCOUNT_ID - ) - result["LayerVersions"].should.equal( - [ - { - "Version": 1, - "LayerVersionArn": expected_arn + "1", - "CompatibleRuntimes": ["python3.6"], - "Description": "", - "LicenseInfo": "MIT", - }, - { - "Version": 2, - "LayerVersionArn": expected_arn + "2", - "CompatibleRuntimes": ["python3.6"], - "Description": "", - "LicenseInfo": "MIT", - }, - ] - ) - - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - Environment={"Variables": {"test_variable": "test_value"}}, - Layers=[(expected_arn + "1")], - ) - - result = conn.get_function_configuration(FunctionName="testFunction") - result["Layers"].should.equal( - [{"Arn": (expected_arn + "1"), "CodeSize": len(zip_content)}] - ) - result = conn.update_function_configuration( - FunctionName="testFunction", Layers=[(expected_arn + "2")] - ) - result["Layers"].should.equal( - [{"Arn": (expected_arn + "2"), "CodeSize": len(zip_content)}] - ) - - # Test get layer versions for non existant layer - result = conn.list_layer_versions(LayerName="testLayer2") - result["LayerVersions"].should.equal([]) - - # Test create function with non existant layer version - with pytest.raises((ValueError, ClientError)): - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=get_role_name(), - Handler="lambda_function.lambda_handler", - Code={"S3Bucket": "test-bucket", "S3Key": "test.zip"}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - Environment={"Variables": {"test_variable": "test_value"}}, - Layers=[(expected_arn + "3")], - ) - - -def create_invalid_lambda(role): - conn = boto3.client("lambda", _lambda_region) - zip_content = get_test_zip_file1() - with pytest.raises(ClientError) as err: - conn.create_function( - FunctionName="testFunction", - Runtime="python2.7", - Role=role, - Handler="lambda_function.handler", - Code={"ZipFile": zip_content}, - Description="test lambda function", - Timeout=3, - MemorySize=128, - Publish=True, - ) - return err - - -def get_role_name(): - with mock_iam(): - iam = boto3.client("iam", region_name=_lambda_region) - try: - return iam.get_role(RoleName="my-role")["Role"]["Arn"] - except ClientError: - return iam.create_role( - RoleName="my-role", - AssumeRolePolicyDocument="some policy", - Path="/my-path/", - )["Role"]["Arn"] diff --git a/tests/test_awslambda/test_lambda_concurrency.py b/tests/test_awslambda/test_lambda_concurrency.py new file mode 100644 index 000000000..1085693f1 --- /dev/null +++ b/tests/test_awslambda/test_lambda_concurrency.py @@ -0,0 +1,92 @@ +import boto3 +import pytest +import sure # noqa + +from moto import mock_lambda +from uuid import uuid4 +from .utilities import get_role_name, get_test_zip_file1 + +_lambda_region = "us-west-2" +boto3.setup_default_session(region_name=_lambda_region) + + +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_lambda +def test_put_function_concurrency(key): + expected_concurrency = 15 + function_name = str(uuid4())[0:6] + + conn = boto3.client("lambda", _lambda_region) + f = conn.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=(get_role_name()), + Handler="lambda_function.handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = f[key] + result = conn.put_function_concurrency( + FunctionName=name_or_arn, ReservedConcurrentExecutions=expected_concurrency + ) + + result["ReservedConcurrentExecutions"].should.equal(expected_concurrency) + + +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_lambda +def test_delete_function_concurrency(key): + function_name = str(uuid4())[0:6] + + conn = boto3.client("lambda", _lambda_region) + f = conn.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=(get_role_name()), + Handler="lambda_function.handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = f[key] + conn.put_function_concurrency( + FunctionName=name_or_arn, ReservedConcurrentExecutions=15 + ) + + conn.delete_function_concurrency(FunctionName=name_or_arn) + result = conn.get_function(FunctionName=function_name) + + result.doesnt.have.key("Concurrency") + + +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_lambda +def test_get_function_concurrency(key): + expected_concurrency = 15 + function_name = str(uuid4())[0:6] + + conn = boto3.client("lambda", _lambda_region) + f = conn.create_function( + FunctionName=function_name, + Runtime="python3.8", + Role=(get_role_name()), + Handler="lambda_function.handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = f[key] + conn.put_function_concurrency( + FunctionName=name_or_arn, ReservedConcurrentExecutions=expected_concurrency + ) + + result = conn.get_function_concurrency(FunctionName=name_or_arn) + + result["ReservedConcurrentExecutions"].should.equal(expected_concurrency) diff --git a/tests/test_awslambda/test_lambda_eventsourcemapping.py b/tests/test_awslambda/test_lambda_eventsourcemapping.py new file mode 100644 index 000000000..03f00073c --- /dev/null +++ b/tests/test_awslambda/test_lambda_eventsourcemapping.py @@ -0,0 +1,477 @@ +import botocore.client +import boto3 +import json +import pytest +import time +import sure # noqa +import uuid + +from moto import ( + mock_dynamodb2, + mock_lambda, + mock_logs, + mock_sns, + mock_sqs, +) +from uuid import uuid4 +from .utilities import ( + get_role_name, + get_test_zip_file3, + wait_for_log_msg, + get_test_zip_file_error, +) + +_lambda_region = "us-west-2" +boto3.setup_default_session(region_name=_lambda_region) + + +@mock_logs +@mock_lambda +@mock_sqs +def test_create_event_source_mapping(): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] + ) + + assert response["EventSourceArn"] == queue.attributes["QueueArn"] + assert response["FunctionArn"] == func["FunctionArn"] + assert response["State"] == "Enabled" + + +@pytest.mark.network +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_logs +@mock_lambda +@mock_sqs +def test_invoke_function_from_sqs(key): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = func[key] + + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=name_or_arn + ) + + assert response["EventSourceArn"] == queue.attributes["QueueArn"] + assert response["State"] == "Enabled" + + sqs_client = boto3.client("sqs", region_name="us-east-1") + sqs_client.send_message(QueueUrl=queue.url, MessageBody="test") + + expected_msg = "get_test_zip_file3 success" + log_group = f"/aws/lambda/{function_name}" + msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) + + assert msg_showed_up, ( + expected_msg + + " was not found after sending an SQS message. All logs: " + + str(all_logs) + ) + + +@pytest.mark.network +@mock_logs +@mock_lambda +@mock_dynamodb2 +def test_invoke_function_from_dynamodb_put(): + dynamodb = boto3.client("dynamodb", region_name="us-east-1") + table_name = str(uuid4())[0:6] + "_table" + table = dynamodb.create_table( + TableName=table_name, + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], + StreamSpecification={ + "StreamEnabled": True, + "StreamViewType": "NEW_AND_OLD_IMAGES", + }, + ) + + conn = boto3.client("lambda", region_name="us-east-1") + function_name = str(uuid4())[0:6] + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function executed after a DynamoDB table is updated", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + response = conn.create_event_source_mapping( + EventSourceArn=table["TableDescription"]["LatestStreamArn"], + FunctionName=func["FunctionArn"], + ) + + assert response["EventSourceArn"] == table["TableDescription"]["LatestStreamArn"] + assert response["State"] == "Enabled" + + dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}}) + + expected_msg = "get_test_zip_file3 success" + log_group = f"/aws/lambda/{function_name}" + msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) + + assert msg_showed_up, ( + expected_msg + " was not found after a DDB insert. All logs: " + str(all_logs) + ) + + +@pytest.mark.network +@mock_logs +@mock_lambda +@mock_dynamodb2 +def test_invoke_function_from_dynamodb_update(): + dynamodb = boto3.client("dynamodb", region_name="us-east-1") + table_name = str(uuid4())[0:6] + "_table" + table = dynamodb.create_table( + TableName=table_name, + KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}], + AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}], + StreamSpecification={ + "StreamEnabled": True, + "StreamViewType": "NEW_AND_OLD_IMAGES", + }, + ) + + conn = boto3.client("lambda", region_name="us-east-1") + function_name = str(uuid4())[0:6] + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function executed after a DynamoDB table is updated", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + conn.create_event_source_mapping( + EventSourceArn=table["TableDescription"]["LatestStreamArn"], + FunctionName=func["FunctionArn"], + ) + + dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}}) + log_group = f"/aws/lambda/{function_name}" + expected_msg = "get_test_zip_file3 success" + msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) + assert "Nr_of_records(1)" in all_logs, "Only one item should be inserted" + + dynamodb.update_item( + TableName=table_name, + Key={"id": {"S": "item 1"}}, + UpdateExpression="set #attr = :val", + ExpressionAttributeNames={"#attr": "new_attr"}, + ExpressionAttributeValues={":val": {"S": "new_val"}}, + ) + msg_showed_up, all_logs = wait_for_log_msg(expected_msg, log_group) + + assert msg_showed_up, ( + expected_msg + " was not found after updating DDB. All logs: " + str(all_logs) + ) + assert "Nr_of_records(1)" in all_logs, "Only one item should be updated" + assert ( + "Nr_of_records(2)" not in all_logs + ), "The inserted item should not show up again" + + +@pytest.mark.network +@mock_logs +@mock_lambda +@mock_sqs +def test_invoke_function_from_sqs_exception(): + function_name = str(uuid4())[0:6] + logs_conn = boto3.client("logs", region_name="us-east-1") + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file_error()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] + ) + + assert response["EventSourceArn"] == queue.attributes["QueueArn"] + assert response["State"] == "Enabled" + + entries = [] + for i in range(3): + body = {"uuid": str(uuid.uuid4()), "test": "test_{}".format(i)} + entry = {"Id": str(i), "MessageBody": json.dumps(body)} + entries.append(entry) + + queue.send_messages(Entries=entries) + + start = time.time() + while (time.time() - start) < 30: + result = logs_conn.describe_log_streams( + logGroupName=f"/aws/lambda/{function_name}" + ) + log_streams = result.get("logStreams") + if not log_streams: + time.sleep(1) + continue + assert len(log_streams) >= 1 + + result = logs_conn.get_log_events( + logGroupName=f"/aws/lambda/{function_name}", + logStreamName=log_streams[0]["logStreamName"], + ) + for event in result.get("events"): + if "I failed!" in event["message"]: + messages = queue.receive_messages(MaxNumberOfMessages=10) + # Verify messages are still visible and unprocessed + assert len(messages) == 3 + return + time.sleep(1) + + assert False, "Test Failed" + + +@pytest.mark.network +@mock_logs +@mock_sns +@mock_lambda +def test_invoke_function_from_sns(): + logs_conn = boto3.client("logs", region_name=_lambda_region) + sns_conn = boto3.client("sns", region_name=_lambda_region) + sns_conn.create_topic(Name="some-topic") + topics_json = sns_conn.list_topics() + topics = topics_json["Topics"] + topic_arn = topics[0]["TopicArn"] + + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + result = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + sns_conn.subscribe( + TopicArn=topic_arn, Protocol="lambda", Endpoint=result["FunctionArn"] + ) + + result = sns_conn.publish(TopicArn=topic_arn, Message=json.dumps({})) + + start = time.time() + events = [] + while (time.time() - start) < 10: + result = logs_conn.describe_log_streams( + logGroupName=f"/aws/lambda/{function_name}" + ) + log_streams = result.get("logStreams") + if not log_streams: + time.sleep(1) + continue + + assert len(log_streams) == 1 + result = logs_conn.get_log_events( + logGroupName=f"/aws/lambda/{function_name}", + logStreamName=log_streams[0]["logStreamName"], + ) + events = result.get("events") + for event in events: + if event["message"] == "get_test_zip_file3 success": + return + + time.sleep(1) + + assert False, "Expected message not found in logs:" + str(events) + + +@mock_logs +@mock_lambda +@mock_sqs +def test_list_event_source_mappings(): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] + ) + mappings = conn.list_event_source_mappings(EventSourceArn="123") + mappings["EventSourceMappings"].should.have.length_of(0) + + mappings = conn.list_event_source_mappings( + EventSourceArn=queue.attributes["QueueArn"] + ) + assert len(mappings["EventSourceMappings"]) >= 1 + assert mappings["EventSourceMappings"][0]["UUID"] == response["UUID"] + assert mappings["EventSourceMappings"][0]["FunctionArn"] == func["FunctionArn"] + + +@mock_lambda +@mock_sqs +def test_get_event_source_mapping(): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func["FunctionArn"] + ) + mapping = conn.get_event_source_mapping(UUID=response["UUID"]) + assert mapping["UUID"] == response["UUID"] + assert mapping["FunctionArn"] == func["FunctionArn"] + + conn.get_event_source_mapping.when.called_with(UUID="1").should.throw( + botocore.client.ClientError + ) + + +@mock_lambda +@mock_sqs +def test_update_event_source_mapping(): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func1 = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + func2 = conn.create_function( + FunctionName="testFunction2", + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func1["FunctionArn"] + ) + assert response["FunctionArn"] == func1["FunctionArn"] + assert response["BatchSize"] == 10 + assert response["State"] == "Enabled" + + mapping = conn.update_event_source_mapping( + UUID=response["UUID"], Enabled=False, BatchSize=2, FunctionName="testFunction2" + ) + assert mapping["UUID"] == response["UUID"] + assert mapping["FunctionArn"] == func2["FunctionArn"] + assert mapping["State"] == "Disabled" + assert mapping["BatchSize"] == 2 + + +@mock_lambda +@mock_sqs +def test_delete_event_source_mapping(): + function_name = str(uuid4())[0:6] + sqs = boto3.resource("sqs", region_name="us-east-1") + queue = sqs.create_queue(QueueName=f"{function_name}_queue") + + conn = boto3.client("lambda", region_name="us-east-1") + func1 = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file3()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + response = conn.create_event_source_mapping( + EventSourceArn=queue.attributes["QueueArn"], FunctionName=func1["FunctionArn"] + ) + assert response["FunctionArn"] == func1["FunctionArn"] + assert response["BatchSize"] == 10 + assert response["State"] == "Enabled" + + response = conn.delete_event_source_mapping(UUID=response["UUID"]) + + assert response["State"] == "Deleting" + conn.get_event_source_mapping.when.called_with(UUID=response["UUID"]).should.throw( + botocore.client.ClientError + ) diff --git a/tests/test_awslambda/test_lambda_invoke.py b/tests/test_awslambda/test_lambda_invoke.py new file mode 100644 index 000000000..5288b673a --- /dev/null +++ b/tests/test_awslambda/test_lambda_invoke.py @@ -0,0 +1,296 @@ +import base64 +import botocore.client +import boto3 +import io +import json +import pytest +import sure # noqa +import zipfile + +from moto import ( + mock_lambda, + mock_ec2, + settings, +) +from uuid import uuid4 +from .utilities import ( + get_role_name, + get_test_zip_file_error, + get_test_zip_file1, + get_zip_with_multiple_files, + get_test_zip_file2, +) + +_lambda_region = "us-west-2" +boto3.setup_default_session(region_name=_lambda_region) + + +@pytest.mark.network +@mock_lambda +def test_invoke_function_that_throws_error(): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file_error()}, + ) + + failure_response = conn.invoke( + FunctionName=function_name, Payload=json.dumps({}), LogType="Tail" + ) + + failure_response.should.have.key("FunctionError").being.equal("Handled") + + payload = failure_response["Payload"].read().decode("utf-8") + payload = json.loads(payload) + payload["errorType"].should.equal("Exception") + payload["errorMessage"].should.equal("I failed!") + payload.should.have.key("stackTrace") + + logs = base64.b64decode(failure_response["LogResult"]).decode("utf-8") + logs.should.contain("START RequestId:") + logs.should.contain("I failed!: Exception") + logs.should.contain("Traceback (most recent call last):") + logs.should.contain("END RequestId:") + + +@pytest.mark.network +@pytest.mark.parametrize("invocation_type", [None, "RequestResponse"]) +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_lambda +def test_invoke_requestresponse_function(invocation_type, key): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + fxn = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = fxn[key] + + # Only add invocation-type keyword-argument when provided, otherwise the request + # fails to be validated + kw = {} + if invocation_type: + kw["InvocationType"] = invocation_type + + in_data = {"msg": "So long and thanks for all the fish"} + success_result = conn.invoke( + FunctionName=name_or_arn, Payload=json.dumps(in_data), LogType="Tail", **kw + ) + + if "FunctionError" in success_result: + assert False, success_result["Payload"].read().decode("utf-8") + + success_result["StatusCode"].should.equal(200) + success_result["ResponseMetadata"]["HTTPHeaders"]["content-type"].should.equal( + "application/json" + ) + logs = base64.b64decode(success_result["LogResult"]).decode("utf-8") + + logs.should.contain("START RequestId:") + logs.should.contain("custom log event") + logs.should.contain("END RequestId:") + + payload = success_result["Payload"].read().decode("utf-8") + json.loads(payload).should.equal(in_data) + + # Logs should not be returned by default, only when the LogType-param is supplied + success_result = conn.invoke( + FunctionName=name_or_arn, Payload=json.dumps(in_data), **kw + ) + + success_result["StatusCode"].should.equal(200) + success_result["ResponseMetadata"]["HTTPHeaders"]["content-type"].should.equal( + "application/json" + ) + assert "LogResult" not in success_result + + +@pytest.mark.network +@mock_lambda +def test_invoke_event_function(): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + conn.invoke.when.called_with( + FunctionName="notAFunction", InvocationType="Event", Payload="{}" + ).should.throw(botocore.client.ClientError) + + in_data = {"msg": "So long and thanks for all the fish"} + success_result = conn.invoke( + FunctionName=function_name, InvocationType="Event", Payload=json.dumps(in_data) + ) + success_result["StatusCode"].should.equal(202) + json.loads(success_result["Payload"].read().decode("utf-8")).should.equal(in_data) + + +@pytest.mark.network +@mock_lambda +def test_invoke_function_with_multiple_files_in_zip(): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python3.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_zip_with_multiple_files()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + in_data = {"msg": "So long and thanks for: "} + success_result = conn.invoke( + FunctionName=function_name, InvocationType="Event", Payload=json.dumps(in_data) + ) + json.loads(success_result["Payload"].read().decode("utf-8")).should.equal( + {"msg": "So long and thanks for: stuff"} + ) + + +@pytest.mark.network +@mock_lambda +def test_invoke_dryrun_function(): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file1(),}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + conn.invoke.when.called_with( + FunctionName="notAFunction", InvocationType="Event", Payload="{}" + ).should.throw(botocore.client.ClientError) + + in_data = {"msg": "So long and thanks for all the fish"} + success_result = conn.invoke( + FunctionName=function_name, + InvocationType="DryRun", + Payload=json.dumps(in_data), + ) + success_result["StatusCode"].should.equal(204) + + +if settings.TEST_SERVER_MODE: + + @mock_ec2 + @mock_lambda + def test_invoke_function_get_ec2_volume(): + conn = boto3.resource("ec2", _lambda_region) + vol = conn.create_volume(Size=99, AvailabilityZone=_lambda_region) + vol = conn.Volume(vol.id) + + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python3.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file2()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + + in_data = {"volume_id": vol.id} + result = conn.invoke( + FunctionName=function_name, + InvocationType="RequestResponse", + Payload=json.dumps(in_data), + ) + result["StatusCode"].should.equal(200) + actual_payload = json.loads(result["Payload"].read().decode("utf-8")) + expected_payload = {"id": vol.id, "state": vol.state, "size": vol.size} + actual_payload.should.equal(expected_payload) + + +@pytest.mark.network +@mock_lambda +def test_invoke_lambda_error(): + lambda_fx = """ +def lambda_handler(event, context): + raise Exception('failsauce') + """ + zip_output = io.BytesIO() + zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) + zip_file.writestr("lambda_function.py", lambda_fx) + zip_file.close() + zip_output.seek(0) + + client = boto3.client("lambda", region_name="us-east-1") + client.create_function( + FunctionName="test-lambda-fx", + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Code={"ZipFile": zip_output.read()}, + ) + + result = client.invoke( + FunctionName="test-lambda-fx", InvocationType="RequestResponse", LogType="Tail" + ) + + assert "FunctionError" in result + assert result["FunctionError"] == "Handled" + + +@pytest.mark.network +@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"]) +@mock_lambda +def test_invoke_async_function(key): + conn = boto3.client("lambda", _lambda_region) + function_name = str(uuid4())[0:6] + fxn = conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"ZipFile": get_test_zip_file1()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + name_or_arn = fxn[key] + + success_result = conn.invoke_async( + FunctionName=name_or_arn, InvokeArgs=json.dumps({"test": "event"}) + ) + + success_result["Status"].should.equal(202) diff --git a/tests/test_awslambda/test_lambda_layers.py b/tests/test_awslambda/test_lambda_layers.py new file mode 100644 index 000000000..f10e693d1 --- /dev/null +++ b/tests/test_awslambda/test_lambda_layers.py @@ -0,0 +1,125 @@ +import boto3 +import pytest +import sure # noqa + +from botocore.exceptions import ClientError +from freezegun import freeze_time +from moto import mock_lambda, mock_s3 +from moto.core.exceptions import RESTError +from moto.sts.models import ACCOUNT_ID +from uuid import uuid4 + +from .utilities import get_role_name, get_test_zip_file1 + +_lambda_region = "us-west-2" +boto3.setup_default_session(region_name=_lambda_region) + + +@mock_lambda +@mock_s3 +@freeze_time("2015-01-01 00:00:00") +def test_get_lambda_layers(): + bucket_name = str(uuid4()) + s3_conn = boto3.client("s3", _lambda_region) + s3_conn.create_bucket( + Bucket=bucket_name, + CreateBucketConfiguration={"LocationConstraint": _lambda_region}, + ) + + zip_content = get_test_zip_file1() + s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content) + conn = boto3.client("lambda", _lambda_region) + layer_name = str(uuid4())[0:6] + + with pytest.raises((RESTError, ClientError)): + conn.publish_layer_version( + LayerName=layer_name, + Content={}, + CompatibleRuntimes=["python3.6"], + LicenseInfo="MIT", + ) + conn.publish_layer_version( + LayerName=layer_name, + Content={"ZipFile": get_test_zip_file1()}, + CompatibleRuntimes=["python3.6"], + LicenseInfo="MIT", + ) + conn.publish_layer_version( + LayerName=layer_name, + Content={"S3Bucket": bucket_name, "S3Key": "test.zip"}, + CompatibleRuntimes=["python3.6"], + LicenseInfo="MIT", + ) + + result = conn.list_layer_versions(LayerName=layer_name) + + for version in result["LayerVersions"]: + version.pop("CreatedDate") + result["LayerVersions"].sort(key=lambda x: x["Version"]) + expected_arn = "arn:aws:lambda:{0}:{1}:layer:{2}:".format( + _lambda_region, ACCOUNT_ID, layer_name + ) + result["LayerVersions"].should.equal( + [ + { + "Version": 1, + "LayerVersionArn": expected_arn + "1", + "CompatibleRuntimes": ["python3.6"], + "Description": "", + "LicenseInfo": "MIT", + }, + { + "Version": 2, + "LayerVersionArn": expected_arn + "2", + "CompatibleRuntimes": ["python3.6"], + "Description": "", + "LicenseInfo": "MIT", + }, + ] + ) + + function_name = str(uuid4())[0:6] + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Environment={"Variables": {"test_variable": "test_value"}}, + Layers=[(expected_arn + "1")], + ) + + result = conn.get_function_configuration(FunctionName=function_name) + result["Layers"].should.equal( + [{"Arn": (expected_arn + "1"), "CodeSize": len(zip_content)}] + ) + result = conn.update_function_configuration( + FunctionName=function_name, Layers=[(expected_arn + "2")] + ) + result["Layers"].should.equal( + [{"Arn": (expected_arn + "2"), "CodeSize": len(zip_content)}] + ) + + # Test get layer versions for non existant layer + result = conn.list_layer_versions(LayerName=f"{layer_name}2") + result["LayerVersions"].should.equal([]) + + # Test create function with non existant layer version + with pytest.raises((ValueError, ClientError)): + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=get_role_name(), + Handler="lambda_function.lambda_handler", + Code={"S3Bucket": bucket_name, "S3Key": "test.zip"}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + Environment={"Variables": {"test_variable": "test_value"}}, + Layers=[(expected_arn + "3")], + ) diff --git a/tests/test_awslambda/utilities.py b/tests/test_awslambda/utilities.py new file mode 100644 index 000000000..2f61e2276 --- /dev/null +++ b/tests/test_awslambda/utilities.py @@ -0,0 +1,145 @@ +import boto3 +import io +import pytest +import time +import zipfile + +from botocore.exceptions import ClientError +from moto import settings, mock_iam +from uuid import uuid4 + +_lambda_region = "us-west-2" + + +def _process_lambda(func_str): + zip_output = io.BytesIO() + zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) + zip_file.writestr("lambda_function.py", func_str) + zip_file.close() + zip_output.seek(0) + return zip_output.read() + + +def get_test_zip_file1(): + pfunc = """ +def lambda_handler(event, context): + print("custom log event") + return event +""" + return _process_lambda(pfunc) + + +def get_test_zip_file2(): + func_str = """ +import boto3 + +def lambda_handler(event, context): + ec2 = boto3.resource('ec2', region_name='us-west-2', endpoint_url='http://{base_url}') + + volume_id = event.get('volume_id') + vol = ec2.Volume(volume_id) + + return {{'id': vol.id, 'state': vol.state, 'size': vol.size}} +""".format( + base_url="motoserver:5000" + if settings.TEST_SERVER_MODE + else "ec2.us-west-2.amazonaws.com" + ) + return _process_lambda(func_str) + + +def get_test_zip_file3(): + pfunc = """ +def lambda_handler(event, context): + print("Nr_of_records("+str(len(event['Records']))+")") + print("get_test_zip_file3 success") + return event +""" + return _process_lambda(pfunc) + + +def get_test_zip_file_error(): + pfunc = """ +def lambda_handler(event, context): + raise Exception('I failed!') +""" + return _process_lambda(pfunc) + + +def get_zip_with_multiple_files(): + pfunc = """ +from utilities import util_function +def lambda_handler(event, context): + x = util_function() + event["msg"] = event["msg"] + x + return event +""" + ufunc = """ +def util_function(): + return "stuff" +""" + zip_output = io.BytesIO() + zip_file = zipfile.ZipFile(zip_output, "a", zipfile.ZIP_DEFLATED) + zip_file.writestr("lambda_function.py", pfunc) + zip_file.close() + zip_file = zipfile.ZipFile(zip_output, "a", zipfile.ZIP_DEFLATED) + zip_file.writestr("utilities.py", ufunc) + zip_file.close() + zip_output.seek(0) + return zip_output.read() + + +def create_invalid_lambda(role): + conn = boto3.client("lambda", _lambda_region) + zip_content = get_test_zip_file1() + function_name = str(uuid4())[0:6] + with pytest.raises(ClientError) as err: + conn.create_function( + FunctionName=function_name, + Runtime="python2.7", + Role=role, + Handler="lambda_function.handler", + Code={"ZipFile": zip_content}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + Publish=True, + ) + return err + + +def get_role_name(): + with mock_iam(): + iam = boto3.client("iam", region_name=_lambda_region) + try: + return iam.get_role(RoleName="my-role")["Role"]["Arn"] + except ClientError: + return iam.create_role( + RoleName="my-role", + AssumeRolePolicyDocument="some policy", + Path="/my-path/", + )["Role"]["Arn"] + + +def wait_for_log_msg(expected_msg, log_group): + logs_conn = boto3.client("logs", region_name="us-east-1") + received_messages = [] + start = time.time() + while (time.time() - start) < 30: + result = logs_conn.describe_log_streams(logGroupName=log_group) + log_streams = result.get("logStreams") + if not log_streams: + time.sleep(1) + continue + + for log_stream in log_streams: + result = logs_conn.get_log_events( + logGroupName=log_group, logStreamName=log_stream["logStreamName"], + ) + received_messages.extend( + [event["message"] for event in result.get("events")] + ) + if expected_msg in received_messages: + return True, received_messages + time.sleep(1) + return False, received_messages diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py index 3afc23c06..52058f3b8 100644 --- a/tests/test_secretsmanager/test_secretsmanager.py +++ b/tests/test_secretsmanager/test_secretsmanager.py @@ -648,7 +648,7 @@ def test_rotate_secret_rotation_period_too_long(): def get_rotation_zip_file(): - from tests.test_awslambda.test_lambda import _process_lambda + from tests.test_awslambda.utilities import _process_lambda func_str = """ import boto3 @@ -723,7 +723,7 @@ if settings.TEST_SERVER_MODE: @mock_lambda @mock_secretsmanager def test_rotate_secret_using_lambda(): - from tests.test_awslambda.test_lambda import get_role_name + from tests.test_awslambda.utilities import get_role_name # Passing a `RotationLambdaARN` value to `rotate_secret` should invoke lambda lambda_conn = boto3.client(