Merge pull request #542 from spulec/feature/lambda
AWS Lambda basic support: ListFunctions, CreateFunction, GetFunction, DeleteFunction
This commit is contained in:
		
						commit
						dba038683b
					
				@ -83,6 +83,8 @@ It gets even better! Moto isn't just S3. Here's the status of the other AWS serv
 | 
			
		||||
|------------------------------------------------------------------------------|
 | 
			
		||||
| IAM                   | @mock_iam        | core endpoints done               |
 | 
			
		||||
|------------------------------------------------------------------------------|
 | 
			
		||||
| Lambda                | @mock_lambda     | basic endpoints done              |
 | 
			
		||||
|------------------------------------------------------------------------------|
 | 
			
		||||
| Kinesis               | @mock_kinesis    | core endpoints done               |
 | 
			
		||||
|------------------------------------------------------------------------------|
 | 
			
		||||
| RDS                   | @mock_rds        | core endpoints done               |
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ __title__ = 'moto'
 | 
			
		||||
__version__ = '0.4.21'
 | 
			
		||||
 | 
			
		||||
from .autoscaling import mock_autoscaling  # flake8: noqa
 | 
			
		||||
from .awslambda import mock_lambda  # flake8: noqa
 | 
			
		||||
from .cloudformation import mock_cloudformation  # flake8: noqa
 | 
			
		||||
from .cloudwatch import mock_cloudwatch  # flake8: noqa
 | 
			
		||||
from .datapipeline import mock_datapipeline  # flake8: noqa
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								moto/awslambda/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								moto/awslambda/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
from .models import lambda_backends
 | 
			
		||||
from ..core.models import MockAWS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lambda_backend = lambda_backends['us-east-1']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mock_lambda(func=None):
 | 
			
		||||
    if func:
 | 
			
		||||
        return MockAWS(lambda_backends)(func)
 | 
			
		||||
    else:
 | 
			
		||||
        return MockAWS(lambda_backends)
 | 
			
		||||
							
								
								
									
										143
									
								
								moto/awslambda/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								moto/awslambda/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,143 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
import datetime
 | 
			
		||||
import hashlib
 | 
			
		||||
 | 
			
		||||
import boto.awslambda
 | 
			
		||||
from moto.core import BaseBackend
 | 
			
		||||
from moto.s3.models import s3_backend
 | 
			
		||||
from moto.s3.exceptions import MissingBucket
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LambdaFunction(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, spec):
 | 
			
		||||
        # required
 | 
			
		||||
        self.code = spec['Code']
 | 
			
		||||
        self.function_name = spec['FunctionName']
 | 
			
		||||
        self.handler = spec['Handler']
 | 
			
		||||
        self.role = spec['Role']
 | 
			
		||||
        self.run_time = spec['Runtime']
 | 
			
		||||
 | 
			
		||||
        # optional
 | 
			
		||||
        self.description = spec.get('Description', '')
 | 
			
		||||
        self.memory_size = spec.get('MemorySize', 128)
 | 
			
		||||
        self.publish = spec.get('Publish', False) # this is ignored currently
 | 
			
		||||
        self.timeout = spec.get('Timeout', 3)
 | 
			
		||||
 | 
			
		||||
        # this isn't finished yet. it needs to find out the VpcId value
 | 
			
		||||
        self._vpc_config = spec.get('VpcConfig', {'SubnetIds': [], 'SecurityGroupIds': []})
 | 
			
		||||
 | 
			
		||||
        # auto-generated
 | 
			
		||||
        self.version = '$LATEST'
 | 
			
		||||
        self.last_modified = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
        if 'ZipFile' in self.code:
 | 
			
		||||
            code = base64.b64decode(self.code['ZipFile'])
 | 
			
		||||
            self.code_size = len(code)
 | 
			
		||||
            self.code_sha_256 = hashlib.sha256(code).hexdigest()
 | 
			
		||||
        else:
 | 
			
		||||
            # validate s3 bucket
 | 
			
		||||
            try:
 | 
			
		||||
                # FIXME: does not validate bucket region
 | 
			
		||||
                key = s3_backend.get_key(self.code['S3Bucket'], self.code['S3Key'])
 | 
			
		||||
            except MissingBucket:
 | 
			
		||||
                raise ValueError(
 | 
			
		||||
                    "InvalidParameterValueException",
 | 
			
		||||
                    "Error occurred while GetObject. S3 Error Code: NoSuchBucket. S3 Error Message: The specified bucket does not exist")
 | 
			
		||||
            else:
 | 
			
		||||
                # validate s3 key
 | 
			
		||||
                if key is None:
 | 
			
		||||
                    raise ValueError(
 | 
			
		||||
                        "InvalidParameterValueException",
 | 
			
		||||
                        "Error occurred while GetObject. S3 Error Code: NoSuchKey. S3 Error Message: The specified key does not exist.")
 | 
			
		||||
                else:
 | 
			
		||||
                    self.code_size = key.size
 | 
			
		||||
                    self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
 | 
			
		||||
        self.function_arn = 'arn:aws:lambda:123456789012:function:{0}'.format(self.function_name)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def vpc_config(self):
 | 
			
		||||
        config = self._vpc_config.copy()
 | 
			
		||||
        if config['SecurityGroupIds']:
 | 
			
		||||
            config.update({"VpcId": "vpc-123abc"})
 | 
			
		||||
        return config
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return json.dumps(self.get_configuration())
 | 
			
		||||
 | 
			
		||||
    def get_configuration(self):
 | 
			
		||||
        return {
 | 
			
		||||
            "CodeSha256": self.code_sha_256,
 | 
			
		||||
            "CodeSize": self.code_size,
 | 
			
		||||
            "Description": self.description,
 | 
			
		||||
            "FunctionArn": self.function_arn,
 | 
			
		||||
            "FunctionName": self.function_name,
 | 
			
		||||
            "Handler": self.handler,
 | 
			
		||||
            "LastModified": self.last_modified,
 | 
			
		||||
            "MemorySize": self.memory_size,
 | 
			
		||||
            "Role": self.role,
 | 
			
		||||
            "Runtime": self.run_time,
 | 
			
		||||
            "Timeout": self.timeout,
 | 
			
		||||
            "Version": self.version,
 | 
			
		||||
            "VpcConfig": self.vpc_config,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def get_code(self):
 | 
			
		||||
        return {
 | 
			
		||||
            "Code": {
 | 
			
		||||
                "Location": "s3://lambda-functions.aws.amazon.com/{0}".format(self.code['S3Key']),
 | 
			
		||||
                "RepositoryType": "S3"
 | 
			
		||||
            },
 | 
			
		||||
            "Configuration": self.get_configuration(),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
 | 
			
		||||
        properties = cloudformation_json['Properties']
 | 
			
		||||
 | 
			
		||||
        # required
 | 
			
		||||
        spec = {
 | 
			
		||||
            'Code': properties['Code'],
 | 
			
		||||
            'FunctionName': resource_name,
 | 
			
		||||
            'Handler': properties['Handler'],
 | 
			
		||||
            'Role': properties['Role'],
 | 
			
		||||
            'Runtime': properties['Runtime'],
 | 
			
		||||
        }
 | 
			
		||||
        optional_properties = 'Description MemorySize Publish Timeout VpcConfig'.split()
 | 
			
		||||
        # NOTE: Not doing `properties.get(k, DEFAULT)` to avoid duplicating the default logic
 | 
			
		||||
        for prop in optional_properties:
 | 
			
		||||
            if prop in properties:
 | 
			
		||||
                spec[prop] = properties[prop]
 | 
			
		||||
 | 
			
		||||
        backend = lambda_backends[region_name]
 | 
			
		||||
        fn = backend.create_function(spec)
 | 
			
		||||
        return fn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LambdaBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self._functions = {}
 | 
			
		||||
    
 | 
			
		||||
    def has_function(self, function_name):
 | 
			
		||||
        return function_name in self._functions
 | 
			
		||||
 | 
			
		||||
    def create_function(self, spec):
 | 
			
		||||
        fn = LambdaFunction(spec)
 | 
			
		||||
        self._functions[fn.function_name] = fn
 | 
			
		||||
        return fn
 | 
			
		||||
 | 
			
		||||
    def get_function(self, function_name):
 | 
			
		||||
        return self._functions[function_name]
 | 
			
		||||
 | 
			
		||||
    def delete_function(self, function_name):
 | 
			
		||||
        del self._functions[function_name]
 | 
			
		||||
 | 
			
		||||
    def list_functions(self):
 | 
			
		||||
        return self._functions.values()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lambda_backends = {}
 | 
			
		||||
for region in boto.awslambda.regions():
 | 
			
		||||
    lambda_backends[region.name] = LambdaBackend()
 | 
			
		||||
							
								
								
									
										82
									
								
								moto/awslambda/responses.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								moto/awslambda/responses.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import re
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
from moto.core.responses import BaseResponse
 | 
			
		||||
from .models import lambda_backends
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LambdaResponse(BaseResponse):
 | 
			
		||||
    
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def root(cls, request, full_url, headers):
 | 
			
		||||
        if request.method == 'GET':
 | 
			
		||||
            return cls()._list_functions(request, full_url, headers)
 | 
			
		||||
        elif request.method == 'POST':
 | 
			
		||||
            return cls()._create_function(request, full_url, headers)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("Cannot handle request")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def function(cls, request, full_url, headers):
 | 
			
		||||
        if request.method == 'GET':
 | 
			
		||||
            return cls()._get_function(request, full_url, headers)
 | 
			
		||||
        elif request.method == 'DELETE':
 | 
			
		||||
            return cls()._delete_function(request, full_url, headers)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("Cannot handle request")
 | 
			
		||||
 | 
			
		||||
    def _list_functions(self, request, full_url, headers):
 | 
			
		||||
        lambda_backend = self.get_lambda_backend(full_url)
 | 
			
		||||
        return 200, headers, json.dumps({
 | 
			
		||||
            "Functions": [fn.get_configuration() for fn in lambda_backend.list_functions()],
 | 
			
		||||
            "NextMarker": str(uuid.uuid4()),
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    def _create_function(self, request, full_url, headers):
 | 
			
		||||
        lambda_backend = self.get_lambda_backend(full_url)
 | 
			
		||||
        spec = json.loads(request.body.decode('utf-8'))
 | 
			
		||||
        try:
 | 
			
		||||
            fn = lambda_backend.create_function(spec)
 | 
			
		||||
        except ValueError as e:
 | 
			
		||||
            return 400, headers, json.dumps({"Error": {"Code": e.args[0], "Message": e.args[1]}})
 | 
			
		||||
        else:
 | 
			
		||||
            config = fn.get_configuration()
 | 
			
		||||
            return 201, headers, json.dumps(config)
 | 
			
		||||
 | 
			
		||||
    def _delete_function(self, request, full_url, headers):
 | 
			
		||||
        lambda_backend = self.get_lambda_backend(full_url)
 | 
			
		||||
 | 
			
		||||
        function_name = request.path.split('/')[-1]
 | 
			
		||||
 | 
			
		||||
        if lambda_backend.has_function(function_name):
 | 
			
		||||
            lambda_backend.delete_function(function_name)
 | 
			
		||||
            return 204, headers, ""
 | 
			
		||||
        else:
 | 
			
		||||
            return 404, headers, "{}"
 | 
			
		||||
 | 
			
		||||
    def _get_function(self, request, full_url, headers):
 | 
			
		||||
        lambda_backend = self.get_lambda_backend(full_url)
 | 
			
		||||
 | 
			
		||||
        function_name = request.path.split('/')[-1]
 | 
			
		||||
 | 
			
		||||
        if lambda_backend.has_function(function_name):
 | 
			
		||||
            fn = lambda_backend.get_function(function_name)
 | 
			
		||||
            code = fn.get_code()
 | 
			
		||||
            return 200, headers, json.dumps(code)
 | 
			
		||||
        else:
 | 
			
		||||
            return 404, headers, "{}"
 | 
			
		||||
    
 | 
			
		||||
    def get_lambda_backend(self, full_url):
 | 
			
		||||
        from moto.awslambda.models import lambda_backends
 | 
			
		||||
        region = self._get_aws_region(full_url)
 | 
			
		||||
        return lambda_backends[region]
 | 
			
		||||
 | 
			
		||||
    def _get_aws_region(self, full_url):
 | 
			
		||||
        region = re.search(self.region_regex, full_url)
 | 
			
		||||
        if region:
 | 
			
		||||
            return region.group(1)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.default_region
 | 
			
		||||
							
								
								
									
										12
									
								
								moto/awslambda/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								moto/awslambda/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
from .responses import LambdaResponse
 | 
			
		||||
 | 
			
		||||
url_bases = [
 | 
			
		||||
    "https?://lambda.(.+).amazonaws.com",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
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<function_name>[\w_-]+)/?$': LambdaResponse.function,
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
from moto.autoscaling import autoscaling_backend
 | 
			
		||||
from moto.awslambda import lambda_backend
 | 
			
		||||
from moto.cloudwatch import cloudwatch_backend
 | 
			
		||||
from moto.cloudformation import cloudformation_backend
 | 
			
		||||
from moto.datapipeline import datapipeline_backend
 | 
			
		||||
@ -43,7 +44,8 @@ BACKENDS = {
 | 
			
		||||
    'sns': sns_backend,
 | 
			
		||||
    'sqs': sqs_backend,
 | 
			
		||||
    'sts': sts_backend,
 | 
			
		||||
    'route53': route53_backend
 | 
			
		||||
    'route53': route53_backend,
 | 
			
		||||
    'lambda': lambda_backend,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import functools
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from moto.autoscaling import models as autoscaling_models
 | 
			
		||||
from moto.awslambda import models as lambda_models
 | 
			
		||||
from moto.datapipeline import models as datapipeline_models
 | 
			
		||||
from moto.ec2 import models as ec2_models
 | 
			
		||||
from moto.elb import models as elb_models
 | 
			
		||||
@ -21,6 +22,7 @@ from boto.exception import BotoServerError
 | 
			
		||||
MODEL_MAP = {
 | 
			
		||||
    "AWS::AutoScaling::AutoScalingGroup": autoscaling_models.FakeAutoScalingGroup,
 | 
			
		||||
    "AWS::AutoScaling::LaunchConfiguration": autoscaling_models.FakeLaunchConfiguration,
 | 
			
		||||
    "AWS::Lambda::Function": lambda_models.LambdaFunction,
 | 
			
		||||
    "AWS::EC2::EIP": ec2_models.ElasticAddress,
 | 
			
		||||
    "AWS::EC2::Instance": ec2_models.Instance,
 | 
			
		||||
    "AWS::EC2::InternetGateway": ec2_models.InternetGateway,
 | 
			
		||||
 | 
			
		||||
@ -5,5 +5,6 @@ sure>=1.2.24
 | 
			
		||||
coverage
 | 
			
		||||
freezegun
 | 
			
		||||
flask
 | 
			
		||||
boto3
 | 
			
		||||
boto3>=1.2.3
 | 
			
		||||
botocore>=1.3.26
 | 
			
		||||
six
 | 
			
		||||
							
								
								
									
										301
									
								
								tests/test_awslambda/test_lambda.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								tests/test_awslambda/test_lambda.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,301 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import botocore.client
 | 
			
		||||
import boto3
 | 
			
		||||
import hashlib
 | 
			
		||||
import io
 | 
			
		||||
import zipfile
 | 
			
		||||
import sure  # noqa
 | 
			
		||||
 | 
			
		||||
from freezegun import freeze_time
 | 
			
		||||
from moto import mock_lambda, mock_s3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_test_zip_file():
 | 
			
		||||
    zip_output = io.BytesIO()
 | 
			
		||||
    zip_file = zipfile.ZipFile(zip_output, 'w')
 | 
			
		||||
    zip_file.writestr('lambda_function.py', b'''\
 | 
			
		||||
def handler(event, context):
 | 
			
		||||
    return "hello world"
 | 
			
		||||
''')
 | 
			
		||||
    zip_file.close()
 | 
			
		||||
    zip_output.seek(0)
 | 
			
		||||
    return zip_output.read()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
def test_list_functions():
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    result = conn.list_functions()
 | 
			
		||||
 | 
			
		||||
    result['Functions'].should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@freeze_time('2015-01-01 00:00:00')
 | 
			
		||||
def test_create_based_on_s3_with_missing_bucket():
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    conn.create_function.when.called_with(
 | 
			
		||||
        FunctionName='testFunction',
 | 
			
		||||
        Runtime='python2.7',
 | 
			
		||||
        Role='test-iam-role',
 | 
			
		||||
        Handler='lambda_function.handler',
 | 
			
		||||
        Code={
 | 
			
		||||
            'S3Bucket': 'this-bucket-does-not-exist',
 | 
			
		||||
            'S3Key': 'test.zip',
 | 
			
		||||
        },
 | 
			
		||||
        Description='test lambda function',
 | 
			
		||||
        Timeout=3,
 | 
			
		||||
        MemorySize=128,
 | 
			
		||||
        Publish=True,
 | 
			
		||||
        VpcConfig={
 | 
			
		||||
            "SecurityGroupIds": ["sg-123abc"],
 | 
			
		||||
            "SubnetIds": ["subnet-123abc"],
 | 
			
		||||
        },
 | 
			
		||||
    ).should.throw(botocore.client.ClientError)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@mock_s3
 | 
			
		||||
@freeze_time('2015-01-01 00:00:00')
 | 
			
		||||
def test_create_function_from_aws_bucket():
 | 
			
		||||
    s3_conn = boto3.client('s3', 'us-west-2')
 | 
			
		||||
    s3_conn.create_bucket(Bucket='test-bucket')
 | 
			
		||||
 | 
			
		||||
    zip_content = get_test_zip_file()
 | 
			
		||||
    s3_conn.put_object(Bucket='test-bucket', Key='test.zip', Body=zip_content)
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    result = conn.create_function(
 | 
			
		||||
        FunctionName='testFunction',
 | 
			
		||||
        Runtime='python2.7',
 | 
			
		||||
        Role='test-iam-role',
 | 
			
		||||
        Handler='lambda_function.handler',
 | 
			
		||||
        Code={
 | 
			
		||||
            'S3Bucket': 'test-bucket',
 | 
			
		||||
            'S3Key': 'test.zip',
 | 
			
		||||
        },
 | 
			
		||||
        Description='test lambda function',
 | 
			
		||||
        Timeout=3,
 | 
			
		||||
        MemorySize=128,
 | 
			
		||||
        Publish=True,
 | 
			
		||||
        VpcConfig={
 | 
			
		||||
            "SecurityGroupIds": ["sg-123abc"],
 | 
			
		||||
            "SubnetIds": ["subnet-123abc"],
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    result.should.equal({
 | 
			
		||||
        'FunctionName': 'testFunction',
 | 
			
		||||
        'FunctionArn': 'arn:aws:lambda:123456789012:function:testFunction',
 | 
			
		||||
        'Runtime': 'python2.7',
 | 
			
		||||
        'Role': 'test-iam-role',
 | 
			
		||||
        'Handler': 'lambda_function.handler',
 | 
			
		||||
        "CodeSha256": hashlib.sha256(zip_content).hexdigest(),
 | 
			
		||||
        "CodeSize": len(zip_content),
 | 
			
		||||
        'Description': 'test lambda function',
 | 
			
		||||
        'Timeout': 3,
 | 
			
		||||
        'MemorySize': 128,
 | 
			
		||||
        'LastModified': '2015-01-01 00:00:00',
 | 
			
		||||
        'Version': '$LATEST',
 | 
			
		||||
        'VpcConfig': {
 | 
			
		||||
            "SecurityGroupIds": ["sg-123abc"],
 | 
			
		||||
            "SubnetIds": ["subnet-123abc"],
 | 
			
		||||
            "VpcId": "vpc-123abc"
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        'ResponseMetadata': {'HTTPStatusCode': 201},
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@freeze_time('2015-01-01 00:00:00')
 | 
			
		||||
def test_create_function_from_zipfile():
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    zip_content = get_test_zip_file()
 | 
			
		||||
    result = 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,
 | 
			
		||||
    )
 | 
			
		||||
    result.should.equal({
 | 
			
		||||
        'FunctionName': 'testFunction',
 | 
			
		||||
        'FunctionArn': 'arn:aws:lambda:123456789012:function:testFunction',
 | 
			
		||||
        'Runtime': 'python2.7',
 | 
			
		||||
        'Role': 'test-iam-role',
 | 
			
		||||
        'Handler': 'lambda_function.handler',
 | 
			
		||||
        'CodeSize': len(zip_content),
 | 
			
		||||
        'Description': 'test lambda function',
 | 
			
		||||
        'Timeout': 3,
 | 
			
		||||
        'MemorySize': 128,
 | 
			
		||||
        'LastModified': '2015-01-01 00:00:00',
 | 
			
		||||
        'CodeSha256': hashlib.sha256(zip_content).hexdigest(),
 | 
			
		||||
        'Version': '$LATEST',
 | 
			
		||||
        'VpcConfig': {
 | 
			
		||||
            "SecurityGroupIds": [],
 | 
			
		||||
            "SubnetIds": [],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        'ResponseMetadata': {'HTTPStatusCode': 201},
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@mock_s3
 | 
			
		||||
@freeze_time('2015-01-01 00:00:00')
 | 
			
		||||
def test_get_function():
 | 
			
		||||
    s3_conn = boto3.client('s3', 'us-west-2')
 | 
			
		||||
    s3_conn.create_bucket(Bucket='test-bucket')
 | 
			
		||||
 | 
			
		||||
    zip_content = get_test_zip_file()
 | 
			
		||||
    s3_conn.put_object(Bucket='test-bucket', Key='test.zip', Body=zip_content)
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    conn.create_function(
 | 
			
		||||
        FunctionName='testFunction',
 | 
			
		||||
        Runtime='python2.7',
 | 
			
		||||
        Role='test-iam-role',
 | 
			
		||||
        Handler='lambda_function.handler',
 | 
			
		||||
        Code={
 | 
			
		||||
            'S3Bucket': 'test-bucket',
 | 
			
		||||
            'S3Key': 'test.zip',
 | 
			
		||||
        },
 | 
			
		||||
        Description='test lambda function',
 | 
			
		||||
        Timeout=3,
 | 
			
		||||
        MemorySize=128,
 | 
			
		||||
        Publish=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    result = conn.get_function(FunctionName='testFunction')
 | 
			
		||||
 | 
			
		||||
    result.should.equal({
 | 
			
		||||
        "Code": {
 | 
			
		||||
            "Location": "s3://lambda-functions.aws.amazon.com/test.zip",
 | 
			
		||||
            "RepositoryType": "S3"
 | 
			
		||||
        },
 | 
			
		||||
        "Configuration": {
 | 
			
		||||
            "CodeSha256": hashlib.sha256(zip_content).hexdigest(),
 | 
			
		||||
            "CodeSize": len(zip_content),
 | 
			
		||||
            "Description": "test lambda function",
 | 
			
		||||
            "FunctionArn": "arn:aws:lambda:123456789012:function:testFunction",
 | 
			
		||||
            "FunctionName": "testFunction",
 | 
			
		||||
            "Handler": "lambda_function.handler",
 | 
			
		||||
            "LastModified": "2015-01-01 00:00:00",
 | 
			
		||||
            "MemorySize": 128,
 | 
			
		||||
            "Role": "test-iam-role",
 | 
			
		||||
            "Runtime": "python2.7",
 | 
			
		||||
            "Timeout": 3,
 | 
			
		||||
            "Version": '$LATEST',
 | 
			
		||||
            "VpcConfig": {
 | 
			
		||||
                "SecurityGroupIds": [],
 | 
			
		||||
                "SubnetIds": [],
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        'ResponseMetadata': {'HTTPStatusCode': 200},
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@mock_s3
 | 
			
		||||
def test_delete_function():
 | 
			
		||||
    s3_conn = boto3.client('s3', 'us-west-2')
 | 
			
		||||
    s3_conn.create_bucket(Bucket='test-bucket')
 | 
			
		||||
 | 
			
		||||
    zip_content = get_test_zip_file()
 | 
			
		||||
    s3_conn.put_object(Bucket='test-bucket', Key='test.zip', Body=zip_content)
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    conn.create_function(
 | 
			
		||||
        FunctionName='testFunction',
 | 
			
		||||
        Runtime='python2.7',
 | 
			
		||||
        Role='test-iam-role',
 | 
			
		||||
        Handler='lambda_function.handler',
 | 
			
		||||
        Code={
 | 
			
		||||
            'S3Bucket': 'test-bucket',
 | 
			
		||||
            'S3Key': 'test.zip',
 | 
			
		||||
        },
 | 
			
		||||
        Description='test lambda function',
 | 
			
		||||
        Timeout=3,
 | 
			
		||||
        MemorySize=128,
 | 
			
		||||
        Publish=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    success_result = conn.delete_function(FunctionName='testFunction')
 | 
			
		||||
    success_result.should.equal({'ResponseMetadata': {'HTTPStatusCode': 204}})
 | 
			
		||||
 | 
			
		||||
    conn.delete_function.when.called_with(FunctionName='testFunctionThatDoesntExist').should.throw(botocore.client.ClientError)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_lambda
 | 
			
		||||
@mock_s3
 | 
			
		||||
@freeze_time('2015-01-01 00:00:00')
 | 
			
		||||
def test_list_create_list_get_delete_list():
 | 
			
		||||
    """
 | 
			
		||||
    test `list -> create -> list -> get -> delete -> list` integration
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    s3_conn = boto3.client('s3', 'us-west-2')
 | 
			
		||||
    s3_conn.create_bucket(Bucket='test-bucket')
 | 
			
		||||
 | 
			
		||||
    zip_content = get_test_zip_file()
 | 
			
		||||
    s3_conn.put_object(Bucket='test-bucket', Key='test.zip', Body=zip_content)
 | 
			
		||||
    conn = boto3.client('lambda', 'us-west-2')
 | 
			
		||||
 | 
			
		||||
    conn.list_functions()['Functions'].should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
    conn.create_function(
 | 
			
		||||
        FunctionName='testFunction',
 | 
			
		||||
        Runtime='python2.7',
 | 
			
		||||
        Role='test-iam-role',
 | 
			
		||||
        Handler='lambda_function.handler',
 | 
			
		||||
        Code={
 | 
			
		||||
            'S3Bucket': 'test-bucket',
 | 
			
		||||
            'S3Key': 'test.zip',
 | 
			
		||||
        },
 | 
			
		||||
        Description='test lambda function',
 | 
			
		||||
        Timeout=3,
 | 
			
		||||
        MemorySize=128,
 | 
			
		||||
        Publish=True,
 | 
			
		||||
    )
 | 
			
		||||
    expected_function_result = {
 | 
			
		||||
        "Code": {
 | 
			
		||||
            "Location": "s3://lambda-functions.aws.amazon.com/test.zip",
 | 
			
		||||
            "RepositoryType": "S3"
 | 
			
		||||
        },
 | 
			
		||||
        "Configuration": {
 | 
			
		||||
            "CodeSha256": hashlib.sha256(zip_content).hexdigest(),
 | 
			
		||||
            "CodeSize": len(zip_content),
 | 
			
		||||
            "Description": "test lambda function",
 | 
			
		||||
            "FunctionArn": "arn:aws:lambda:123456789012:function:testFunction",
 | 
			
		||||
            "FunctionName": "testFunction",
 | 
			
		||||
            "Handler": "lambda_function.handler",
 | 
			
		||||
            "LastModified": "2015-01-01 00:00:00",
 | 
			
		||||
            "MemorySize": 128,
 | 
			
		||||
            "Role": "test-iam-role",
 | 
			
		||||
            "Runtime": "python2.7",
 | 
			
		||||
            "Timeout": 3,
 | 
			
		||||
            "Version": '$LATEST',
 | 
			
		||||
            "VpcConfig": {
 | 
			
		||||
                "SecurityGroupIds": [],
 | 
			
		||||
                "SubnetIds": [],
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        'ResponseMetadata': {'HTTPStatusCode': 200},
 | 
			
		||||
    }
 | 
			
		||||
    conn.list_functions()['Functions'].should.equal([expected_function_result['Configuration']])
 | 
			
		||||
 | 
			
		||||
    conn.get_function(FunctionName='testFunction').should.equal(expected_function_result)
 | 
			
		||||
    conn.delete_function(FunctionName='testFunction')
 | 
			
		||||
 | 
			
		||||
    conn.list_functions()['Functions'].should.have.length_of(0)
 | 
			
		||||
@ -13,6 +13,7 @@ import boto.redshift
 | 
			
		||||
import boto.sns
 | 
			
		||||
import boto.sqs
 | 
			
		||||
import boto.vpc
 | 
			
		||||
import boto3
 | 
			
		||||
import sure  # noqa
 | 
			
		||||
 | 
			
		||||
from moto import (
 | 
			
		||||
@ -22,6 +23,7 @@ from moto import (
 | 
			
		||||
    mock_ec2,
 | 
			
		||||
    mock_elb,
 | 
			
		||||
    mock_iam,
 | 
			
		||||
    mock_lambda,
 | 
			
		||||
    mock_rds,
 | 
			
		||||
    mock_redshift,
 | 
			
		||||
    mock_route53,
 | 
			
		||||
@ -1478,3 +1480,49 @@ def test_datapipeline():
 | 
			
		||||
    stack_resources = cf_conn.list_stack_resources(stack_id)
 | 
			
		||||
    stack_resources.should.have.length_of(1)
 | 
			
		||||
    stack_resources[0].physical_resource_id.should.equal(data_pipelines['pipelineIdList'][0]['id'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
@mock_lambda
 | 
			
		||||
def test_lambda_function():
 | 
			
		||||
    conn = boto3.client('lambda', 'us-east-1')
 | 
			
		||||
    template = {
 | 
			
		||||
        "AWSTemplateFormatVersion": "2010-09-09",
 | 
			
		||||
        "Resources": {
 | 
			
		||||
            "lambdaTest": {
 | 
			
		||||
                "Type": "AWS::Lambda::Function",
 | 
			
		||||
                "Properties": {
 | 
			
		||||
                    "Code": {
 | 
			
		||||
                        "ZipFile": {"Fn::Join": [
 | 
			
		||||
                            "\n",
 | 
			
		||||
                            """
 | 
			
		||||
                            exports.handler = function(event, context) {
 | 
			
		||||
                                context.succeed();
 | 
			
		||||
                            }
 | 
			
		||||
                            """.splitlines()
 | 
			
		||||
                        ]}
 | 
			
		||||
                    },
 | 
			
		||||
                    "Handler": "index.handler",
 | 
			
		||||
                    "Description": "Test function",
 | 
			
		||||
                    "MemorySize": 128,
 | 
			
		||||
                    "Role": "test-role",
 | 
			
		||||
                    "Runtime": "nodejs",
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template_json = json.dumps(template)
 | 
			
		||||
    cf_conn = boto.cloudformation.connect_to_region("us-east-1")
 | 
			
		||||
    cf_conn.create_stack(
 | 
			
		||||
        "test_stack",
 | 
			
		||||
        template_body=template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    result = conn.list_functions()
 | 
			
		||||
    result['Functions'].should.have.length_of(1)
 | 
			
		||||
    result['Functions'][0]['Description'].should.equal('Test function')
 | 
			
		||||
    result['Functions'][0]['Handler'].should.equal('index.handler')
 | 
			
		||||
    result['Functions'][0]['MemorySize'].should.equal(128)
 | 
			
		||||
    result['Functions'][0]['Role'].should.equal('test-role')
 | 
			
		||||
    result['Functions'][0]['Runtime'].should.equal('nodejs')
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user