diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py index 5879b9d90..d5be3b3be 100644 --- a/moto/awslambda/models.py +++ b/moto/awslambda/models.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import base64 import datetime import hashlib +import json import boto.awslambda from moto.core import BaseBackend @@ -92,6 +93,18 @@ class LambdaFunction(object): "Configuration": self.get_configuration(), } + def invoke(self, request, headers): + payload = dict() + + # Get the invocation type: + if request.headers.get("x-amz-invocation-type") == "RequestResponse": + encoded = base64.b64encode("Some log file output...".encode('utf-8')) + headers["x-amz-log-result"] = encoded.decode('utf-8') + + payload["result"] = "Good" + + return json.dumps(payload, indent=4) + @classmethod def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] diff --git a/moto/awslambda/responses.py b/moto/awslambda/responses.py index 486f413fe..98458cc2c 100644 --- a/moto/awslambda/responses.py +++ b/moto/awslambda/responses.py @@ -28,6 +28,25 @@ class LambdaResponse(BaseResponse): else: raise ValueError("Cannot handle request") + @classmethod + def invoke(cls, request, full_url, headers): + if request.method == 'POST': + return cls()._invoke(request, full_url, headers) + else: + raise ValueError("Cannot handle request") + + def _invoke(self, request, full_url, headers): + lambda_backend = self.get_lambda_backend(full_url) + + function_name = request.path.split('/')[-2] + + if lambda_backend.has_function(function_name): + fn = lambda_backend.get_function(function_name) + payload = fn.invoke(request, headers) + return 200, headers, payload + else: + return 404, headers, "{}" + def _list_functions(self, request, full_url, headers): lambda_backend = self.get_lambda_backend(full_url) return 200, headers, json.dumps({ diff --git a/moto/awslambda/urls.py b/moto/awslambda/urls.py index 1a9197029..79a99c9f8 100644 --- a/moto/awslambda/urls.py +++ b/moto/awslambda/urls.py @@ -9,4 +9,5 @@ url_paths = { # double curly braces because the `format()` method is called on the strings '{0}/\d{{4}}-\d{{2}}-\d{{2}}/functions/?$': LambdaResponse.root, '{0}/\d{{4}}-\d{{2}}-\d{{2}}/functions/(?P[\w_-]+)/?$': LambdaResponse.function, + '{0}/\d{{4}}-\d{{2}}-\d{{2}}/functions/(?P[\w_-]+)/invocations?$': LambdaResponse.invoke, } diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py index ea4eea310..84abb93f1 100644 --- a/tests/test_awslambda/test_lambda.py +++ b/tests/test_awslambda/test_lambda.py @@ -32,6 +32,43 @@ def test_list_functions(): result['Functions'].should.have.length_of(0) +@mock_lambda +@mock_s3 +@freeze_time('2015-01-01 00:00:00') +def test_invoke_function(): + conn = boto3.client('lambda', 'us-west-2') + + zip_content = get_test_zip_file() + conn.create_function( + FunctionName='testFunction', + Runtime='python2.7', + Role='test-iam-role', + Handler='lambda_function.handler', + Code={ + 'ZipFile': zip_content, + }, + Description='test lambda function', + Timeout=3, + MemorySize=128, + Publish=True, + ) + + success_result = conn.invoke(FunctionName='testFunction', InvocationType='Event', Payload='{}') + success_result["StatusCode"].should.equal(200) + + conn.invoke.when.called_with( + FunctionName='notAFunction', + InvocationType='Event', + Payload='{}' + ).should.throw(botocore.client.ClientError) + + success_result = conn.invoke(FunctionName='testFunction', InvocationType='RequestResponse', Payload='{}') + success_result["StatusCode"].should.equal(200) + + import base64 + base64.b64decode(success_result["LogResult"]).decode('utf-8').should.equal("Some log file output...") + + @mock_lambda @freeze_time('2015-01-01 00:00:00') def test_create_based_on_s3_with_missing_bucket():