From 83f4419d03293261250be9fe0fca04153c58eb4c Mon Sep 17 00:00:00 2001 From: Barry Ruffner Date: Tue, 3 Apr 2018 11:38:59 -0700 Subject: [PATCH] Added create_identity_pool and cleaned up test data. --- moto/__init__.py | 1 + moto/cognitoidentity/__init__.py | 4 +- moto/cognitoidentity/models.py | 188 ++--- moto/cognitoidentity/responses.py | 108 +-- moto/cognitoidentity/urls.py | 6 +- .../test_cognitoidentity.py | 783 +----------------- 6 files changed, 144 insertions(+), 946 deletions(-) diff --git a/moto/__init__.py b/moto/__init__.py index 9703f9f68..8c0de8c91 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -11,6 +11,7 @@ from .autoscaling import mock_autoscaling, mock_autoscaling_deprecated # flake8 from .awslambda import mock_lambda, mock_lambda_deprecated # flake8: noqa from .cloudformation import mock_cloudformation, mock_cloudformation_deprecated # flake8: noqa from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # flake8: noqa +from .cognitoidentity import mock_cognitoidentity, mock_cognitoidentity_deprecated # flake8: noqa from .datapipeline import mock_datapipeline, mock_datapipeline_deprecated # flake8: noqa from .dynamodb import mock_dynamodb, mock_dynamodb_deprecated # flake8: noqa from .dynamodb2 import mock_dynamodb2, mock_dynamodb2_deprecated # flake8: noqa diff --git a/moto/cognitoidentity/__init__.py b/moto/cognitoidentity/__init__.py index f9a6da7ed..2f040fa19 100644 --- a/moto/cognitoidentity/__init__.py +++ b/moto/cognitoidentity/__init__.py @@ -3,5 +3,5 @@ from .models import cognitoidentity_backends from ..core.models import base_decorator, deprecated_base_decorator cognitoidentity_backend = cognitoidentity_backends['us-east-1'] -mock_datapipeline = base_decorator(cognitoidentity_backends) -mock_datapipeline_deprecated = deprecated_base_decorator(cognitoidentity_backends) +mock_cognitoidentity = base_decorator(cognitoidentity_backends) +mock_cognitoidentity_deprecated = deprecated_base_decorator(cognitoidentity_backends) diff --git a/moto/cognitoidentity/models.py b/moto/cognitoidentity/models.py index f136d0799..95663de53 100644 --- a/moto/cognitoidentity/models.py +++ b/moto/cognitoidentity/models.py @@ -1,10 +1,12 @@ from __future__ import unicode_literals +import json import datetime import boto.cognito.identity from moto.compat import OrderedDict from moto.core import BaseBackend, BaseModel -from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys +from moto.core.utils import iso_8601_datetime_with_milliseconds +from .utils import get_random_identity_id, remove_capitalization_of_dict_keys class CognitoIdentityObject(BaseModel): @@ -24,139 +26,91 @@ class CognitoIdentityObject(BaseModel): class CognitoIdentity(BaseModel): - def __init__(self, name, unique_id, **kwargs): - self.name = name - # self.unique_id = unique_id - # self.description = kwargs.get('description', '') - self.identity_pool_id = get_random_identity_id() - # self.creation_time = datetime.datetime.utcnow() - # self.objects = [] - # self.status = "PENDING" - # self.tags = kwargs.get('tags', []) + def __init__(self, region, identity_pool_name, **kwargs): + self.identity_pool_name = identity_pool_name + self.allow_unauthenticated_identities = kwargs.get('allow_unauthenticated_identities', '') + self.supported_login_providers = kwargs.get('supported_login_providers', {}) + self.developer_provider_name = kwargs.get('developer_provider_name', '') + self.open_id_connect_provider_arns = kwargs.get('open_id_connect_provider_arns', []) + self.cognito_identity_providers = kwargs.get('cognito_identity_providers', []) + self.saml_provider_arns = kwargs.get('saml_provider_arns', []) - @property - def physical_resource_id(self): - return self.pipeline_id - - def to_meta_json(self): - return { - "id": self.pipeline_id, - "name": self.name, - } - - def to_json(self): - return { - "description": self.description, - "fields": [{ - "key": "@pipelineState", - "stringValue": self.status, - }, { - "key": "description", - "stringValue": self.description - }, { - "key": "name", - "stringValue": self.name - }, { - "key": "@creationTime", - "stringValue": datetime.datetime.strftime(self.creation_time, '%Y-%m-%dT%H-%M-%S'), - }, { - "key": "@id", - "stringValue": self.pipeline_id, - }, { - "key": "@sphere", - "stringValue": "PIPELINE" - }, { - "key": "@version", - "stringValue": "1" - }, { - "key": "@userId", - "stringValue": "924374875933" - }, { - "key": "@accountId", - "stringValue": "924374875933" - }, { - "key": "uniqueId", - "stringValue": self.unique_id - }], - "name": self.name, - "pipelineId": self.pipeline_id, - "tags": self.tags - } - - def set_pipeline_objects(self, pipeline_objects): - self.objects = [ - PipelineObject(pipeline_object['id'], pipeline_object[ - 'name'], pipeline_object['fields']) - for pipeline_object in remove_capitalization_of_dict_keys(pipeline_objects) - ] - - def activate(self): - self.status = "SCHEDULED" - - @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): - datapipeline_backend = cognitoidentity_backends[region_name] - properties = cloudformation_json["Properties"] - - cloudformation_unique_id = "cf-" + properties["Name"] - pipeline = datapipeline_backend.create_pipeline( - properties["Name"], cloudformation_unique_id) - datapipeline_backend.put_pipeline_definition( - pipeline.pipeline_id, properties["PipelineObjects"]) - - if properties["Activate"]: - pipeline.activate() - return pipeline + self.identity_pool_id = get_random_identity_id(region) + self.creation_time = datetime.datetime.utcnow() class CognitoIdentityBackend(BaseBackend): def __init__(self, region): + super(CognitoIdentityBackend, self).__init__() self.region = region self.identity_pools = OrderedDict() - def create_identity_pool(self, identity_pool_name, allow_unauthenticated_identities, , region='us-east-1',**kwargs): - identity_pool = CognitoIdentity(identity_pool_name, allow_unauthenticated_identities, **kwargs) - self.identity_pools[identity_pool.identity_pool_name] = identity_pool - return identity_pool + def reset(self): + region = self.region + self.__dict__ = {} + self.__init__(region) - def delete_identity_pool(self, identity_pool_id): - pass + def create_identity_pool(self, identity_pool_name, allow_unauthenticated_identities, + supported_login_providers, developer_provider_name, open_id_connect_provider_arns, + cognito_identity_providers, saml_provider_arns): - def list_pipelines(self): - return self.pipelines.values() + new_identity = CognitoIdentity(self.region, identity_pool_name, + allow_unauthenticated_identities=allow_unauthenticated_identities, + supported_login_providers=supported_login_providers, + developer_provider_name=developer_provider_name, + open_id_connect_provider_arns=open_id_connect_provider_arns, + cognito_identity_providers=cognito_identity_providers, + saml_provider_arns=saml_provider_arns) + self.identity_pools[new_identity.identity_pool_id] = new_identity - def describe_pipelines(self, pipeline_ids): - pipelines = [pipeline for pipeline in self.pipelines.values( - ) if pipeline.pipeline_id in pipeline_ids] - return pipelines + response = json.dumps({ + 'IdentityPoolId': new_identity.identity_pool_id, + 'IdentityPoolName': new_identity.identity_pool_name, + 'AllowUnauthenticatedIdentities': new_identity.allow_unauthenticated_identities, + 'SupportedLoginProviders': new_identity.supported_login_providers, + 'DeveloperProviderName': new_identity.developer_provider_name, + 'OpenIdConnectProviderARNs': new_identity.open_id_connect_provider_arns, + 'CognitoIdentityProviders': new_identity.cognito_identity_providers, + 'SamlProviderARNs': new_identity.saml_provider_arns + }) - def get_pipeline(self, pipeline_id): - return self.pipelines[pipeline_id] + return response - def delete_pipeline(self, pipeline_id): - self.pipelines.pop(pipeline_id, None) - def put_pipeline_definition(self, pipeline_id, pipeline_objects): - pipeline = self.get_pipeline(pipeline_id) - pipeline.set_pipeline_objects(pipeline_objects) + def get_id(self): + identity_id = {'IdentityId': get_random_identity_id(self.region)} + return json.dumps(identity_id) - def get_pipeline_definition(self, pipeline_id): - pipeline = self.get_pipeline(pipeline_id) - return pipeline.objects - - def describe_objects(self, object_ids, pipeline_id): - pipeline = self.get_pipeline(pipeline_id) - pipeline_objects = [ - pipeline_object for pipeline_object in pipeline.objects - if pipeline_object.object_id in object_ids - ] - return pipeline_objects - - def activate_pipeline(self, pipeline_id): - pipeline = self.get_pipeline(pipeline_id) - pipeline.activate() + def get_credentials_for_identity(self, identity_id): + duration = 90 + now = datetime.datetime.utcnow() + expiration = now + datetime.timedelta(seconds=duration) + expiration_str = str(iso_8601_datetime_with_milliseconds(expiration)) + return json.dumps({ + "Credentials": { + "AccessKeyId": "TESTACCESSKEY12345", + "Expiration": expiration_str, + "SecretKey": "ABCSECRETKEY", + "SessionToken": "ABC12345" + }, + "IdentityId": identity_id + }) + def get_open_id_token_for_developer_identity(self, identity_id): + duration = 90 + now = datetime.datetime.utcnow() + expiration = now + datetime.timedelta(seconds=duration) + expiration_str = str(iso_8601_datetime_with_milliseconds(expiration)) + return json.dumps({ + "Credentials": { + "AccessKeyId": "TESTACCESSKEY12345", + "Expiration": expiration_str, + "SecretKey": "ABCSECRETKEY", + "SessionToken": "ABC12345" + }, + "IdentityId": identity_id + }) cognitoidentity_backends = {} for region in boto.cognito.identity.regions(): diff --git a/moto/cognitoidentity/responses.py b/moto/cognitoidentity/responses.py index e462e3981..2285607a9 100644 --- a/moto/cognitoidentity/responses.py +++ b/moto/cognitoidentity/responses.py @@ -3,10 +3,10 @@ from __future__ import unicode_literals import json from moto.core.responses import BaseResponse -from .models import datapipeline_backends +from .models import cognitoidentity_backends -class DataPipelineResponse(BaseResponse): +class CognitoIdentityResponse(BaseResponse): @property def parameters(self): @@ -17,88 +17,32 @@ class DataPipelineResponse(BaseResponse): return self.querystring @property - def datapipeline_backend(self): - return datapipeline_backends[self.region] + def cognitoidentity_backend(self): + return cognitoidentity_backends[self.region] - def create_pipeline(self): - name = self.parameters.get('name') - unique_id = self.parameters.get('uniqueId') - description = self.parameters.get('description', '') - tags = self.parameters.get('tags', []) - pipeline = self.datapipeline_backend.create_pipeline(name, unique_id, description=description, tags=tags) - return json.dumps({ - "pipelineId": pipeline.pipeline_id, - }) + def create_identity_pool(self): + identity_pool_name = self._get_param('IdentityPoolName') + allow_unauthenticated_identities = self._get_param('AllowUnauthenticatedIdentities') + supported_login_providers = self._get_param('SupportedLoginProviders') + developer_provider_name = self._get_param('DeveloperProviderName') + open_id_connect_provider_arns = self._get_param('OpenIdConnectProviderARNs') + cognito_identity_providers = self._get_param('CognitoIdentityProviders') + saml_provider_arns = self._get_param('SamlProviderARNs') + return cognitoidentity_backends[self.region].create_identity_pool( + identity_pool_name=identity_pool_name, + allow_unauthenticated_identities=allow_unauthenticated_identities, + supported_login_providers=supported_login_providers, + developer_provider_name=developer_provider_name, + open_id_connect_provider_arns=open_id_connect_provider_arns, + cognito_identity_providers=cognito_identity_providers, + saml_provider_arns=saml_provider_arns) - def list_pipelines(self): - pipelines = list(self.datapipeline_backend.list_pipelines()) - pipeline_ids = [pipeline.pipeline_id for pipeline in pipelines] - max_pipelines = 50 - marker = self.parameters.get('marker') - if marker: - start = pipeline_ids.index(marker) + 1 - else: - start = 0 - pipelines_resp = pipelines[start:start + max_pipelines] - has_more_results = False - marker = None - if start + max_pipelines < len(pipeline_ids) - 1: - has_more_results = True - marker = pipelines_resp[-1].pipeline_id - return json.dumps({ - "hasMoreResults": has_more_results, - "marker": marker, - "pipelineIdList": [ - pipeline.to_meta_json() for pipeline in pipelines_resp - ] - }) + def get_id(self): + return cognitoidentity_backends[self.region].get_id() - def describe_pipelines(self): - pipeline_ids = self.parameters["pipelineIds"] - pipelines = self.datapipeline_backend.describe_pipelines(pipeline_ids) + def get_credentials_for_identity(self): + return cognitoidentity_backends[self.region].get_credentials_for_identity(self._get_param('IdentityId')) - return json.dumps({ - "pipelineDescriptionList": [ - pipeline.to_json() for pipeline in pipelines - ] - }) + def get_open_id_token_for_developer_identity(self): + return cognitoidentity_backends[self.region].get_open_id_token_for_developer_identity(self._get_param('IdentityId')) - def delete_pipeline(self): - pipeline_id = self.parameters["pipelineId"] - self.datapipeline_backend.delete_pipeline(pipeline_id) - return json.dumps({}) - - def put_pipeline_definition(self): - pipeline_id = self.parameters["pipelineId"] - pipeline_objects = self.parameters["pipelineObjects"] - - self.datapipeline_backend.put_pipeline_definition( - pipeline_id, pipeline_objects) - return json.dumps({"errored": False}) - - def get_pipeline_definition(self): - pipeline_id = self.parameters["pipelineId"] - pipeline_definition = self.datapipeline_backend.get_pipeline_definition( - pipeline_id) - return json.dumps({ - "pipelineObjects": [pipeline_object.to_json() for pipeline_object in pipeline_definition] - }) - - def describe_objects(self): - pipeline_id = self.parameters["pipelineId"] - object_ids = self.parameters["objectIds"] - - pipeline_objects = self.datapipeline_backend.describe_objects( - object_ids, pipeline_id) - return json.dumps({ - "hasMoreResults": False, - "marker": None, - "pipelineObjects": [ - pipeline_object.to_json() for pipeline_object in pipeline_objects - ] - }) - - def activate_pipeline(self): - pipeline_id = self.parameters["pipelineId"] - self.datapipeline_backend.activate_pipeline(pipeline_id) - return json.dumps({}) diff --git a/moto/cognitoidentity/urls.py b/moto/cognitoidentity/urls.py index 40805874b..3fe63ef07 100644 --- a/moto/cognitoidentity/urls.py +++ b/moto/cognitoidentity/urls.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals -from .responses import DataPipelineResponse +from .responses import CognitoIdentityResponse url_bases = [ - "https?://datapipeline.(.+).amazonaws.com", + "https?://cognito-identity.(.+).amazonaws.com", ] url_paths = { - '{0}/$': DataPipelineResponse.dispatch, + '{0}/$': CognitoIdentityResponse.dispatch, } diff --git a/tests/test_cognitoidentity/test_cognitoidentity.py b/tests/test_cognitoidentity/test_cognitoidentity.py index e7a9f9174..730f3dccf 100644 --- a/tests/test_cognitoidentity/test_cognitoidentity.py +++ b/tests/test_cognitoidentity/test_cognitoidentity.py @@ -10,753 +10,52 @@ import zipfile import sure # noqa from freezegun import freeze_time -from moto import mock_lambda, mock_s3, mock_ec2, settings +from moto import mock_cognitoidentity, settings -_lambda_region = 'us-west-2' +@mock_cognitoidentity +def test_create_identity_pool(): + conn = boto3.client('cognito-identity', '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): - 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) - - print('get volume details for %s\\nVolume - %s state=%s, size=%s' % (volume_id, volume_id, vol.state, vol.size)) - return event -""".format(base_url="motoserver:5000" if settings.TEST_SERVER_MODE else "ec2.us-west-2.amazonaws.com") - return _process_lambda(func_str) - - -@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 -def test_invoke_requestresponse_function(): - conn = boto3.client('lambda', 'us-west-2') - conn.create_function( - FunctionName='testFunction', - Runtime='python2.7', - Role='test-iam-role', - Handler='lambda_function.lambda_handler', - Code={ - 'ZipFile': get_test_zip_file1(), - }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - - in_data = {'msg': 'So long and thanks for all the fish'} - success_result = conn.invoke(FunctionName='testFunction', InvocationType='RequestResponse', - Payload=json.dumps(in_data)) - - success_result["StatusCode"].should.equal(202) - result_obj = json.loads( - base64.b64decode(success_result["LogResult"]).decode('utf-8')) - - result_obj.should.equal(in_data) - - payload = success_result["Payload"].read().decode('utf-8') - json.loads(payload).should.equal(in_data) - - -@mock_lambda -def test_invoke_event_function(): - conn = boto3.client('lambda', 'us-west-2') - conn.create_function( - FunctionName='testFunction', - Runtime='python2.7', - Role='test-iam-role', - 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({}) - - -if settings.TEST_SERVER_MODE: - @mock_ec2 - @mock_lambda - def test_invoke_function_get_ec2_volume(): - conn = boto3.resource("ec2", "us-west-2") - vol = conn.create_volume(Size=99, AvailabilityZone='us-west-2') - vol = conn.Volume(vol.id) - - conn = boto3.client('lambda', 'us-west-2') - conn.create_function( - FunctionName='testFunction', - Runtime='python2.7', - Role='test-iam-role', - Handler='lambda_function.lambda_handler', - Code={ - 'ZipFile': get_test_zip_file2(), + result = conn.create_identity_pool(IdentityPoolName='TestPool', + AllowUnauthenticatedIdentities=False, + SupportedLoginProviders={'graph.facebook.com':'123456789012345'}, + DeveloperProviderName='devname', + OpenIdConnectProviderARNs=['arn:aws:rds:eu-west-2:123456789012:db:mysql-db',], + CognitoIdentityProviders=[ + { + 'ProviderName': 'testprovider', + 'ClientId': 'CLIENT12345', + 'ServerSideTokenCheck': True }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) + ], + SamlProviderARNs=['arn:aws:rds:eu-west-2:123456789012:db:mysql-db',]) + assert result['IdentityPoolId'] != '' - in_data = {'volume_id': vol.id} - result = conn.invoke(FunctionName='testFunction', - InvocationType='RequestResponse', Payload=json.dumps(in_data)) - result["StatusCode"].should.equal(202) - msg = 'get volume details for %s\nVolume - %s state=%s, size=%s\n%s' % ( - vol.id, vol.id, vol.state, vol.size, json.dumps(in_data)) +@mock_cognitoidentity +def test_get_id(): + conn = boto3.client('cognito-identity', 'us-west-2') + result = conn.get_id(AccountId='someaccount', + IdentityPoolId='us-west-2:12345', + Logins={ + 'someurl': '12345' + }) + assert result['IdentityId'].startswith('us-west-2') - log_result = base64.b64decode(result["LogResult"]).decode('utf-8') +@mock_cognitoidentity +def test_get_credentials_for_identity(): + conn = boto3.client('cognito-identity', 'us-west-2') + result = conn.get_credentials_for_identity(IdentityId='12345') + assert result['IdentityId'] == '12345' - # fix for running under travis (TODO: investigate why it has an extra newline) - log_result = log_result.replace('\n\n', '\n') - log_result.should.equal(msg) - - payload = result['Payload'].read().decode('utf-8') - - # fix for running under travis (TODO: investigate why it has an extra newline) - payload = payload.replace('\n\n', '\n') - payload.should.equal(msg) - - -@mock_lambda -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.lambda_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_file2() - - 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.lambda_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"], +@mock_cognitoidentity +def test_get_open_id_token_for_developer_identity(): + conn = boto3.client('cognito-identity', 'us-west-2') + result = conn.get_open_id_token_for_developer_identity( + IdentityPoolId='us-west-2:12345', + IdentityId='12345', + Logins={ + 'someurl': '12345' }, + TokenDuration=123 ) - # this is hard to match against, so remove it - result['ResponseMetadata'].pop('HTTPHeaders', None) - # Botocore inserts retry attempts not seen in Python27 - result['ResponseMetadata'].pop('RetryAttempts', None) - result.pop('LastModified') - result.should.equal({ - 'FunctionName': 'testFunction', - 'FunctionArn': 'arn:aws:lambda:{}:123456789012:function:testFunction:$LATEST'.format(_lambda_region), - 'Runtime': 'python2.7', - 'Role': 'test-iam-role', - 'Handler': 'lambda_function.lambda_handler', - "CodeSha256": hashlib.sha256(zip_content).hexdigest(), - "CodeSize": len(zip_content), - 'Description': 'test lambda function', - 'Timeout': 3, - 'MemorySize': 128, - '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_file1() - result = conn.create_function( - FunctionName='testFunction', - Runtime='python2.7', - Role='test-iam-role', - Handler='lambda_function.lambda_handler', - Code={ - 'ZipFile': zip_content, - }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - # this is hard to match against, so remove it - result['ResponseMetadata'].pop('HTTPHeaders', None) - # Botocore inserts retry attempts not seen in Python27 - result['ResponseMetadata'].pop('RetryAttempts', None) - result.pop('LastModified') - - result.should.equal({ - 'FunctionName': 'testFunction', - 'FunctionArn': 'arn:aws:lambda:{}:123456789012:function:testFunction:$LATEST'.format(_lambda_region), - 'Runtime': 'python2.7', - 'Role': 'test-iam-role', - 'Handler': 'lambda_function.lambda_handler', - 'CodeSize': len(zip_content), - 'Description': 'test lambda function', - 'Timeout': 3, - 'MemorySize': 128, - '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_file1() - 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.lambda_handler', - Code={ - 'S3Bucket': 'test-bucket', - 'S3Key': 'test.zip', - }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - - result = conn.get_function(FunctionName='testFunction') - # this is hard to match against, so remove it - result['ResponseMetadata'].pop('HTTPHeaders', None) - # Botocore inserts retry attempts not seen in Python27 - result['ResponseMetadata'].pop('RetryAttempts', None) - result['Configuration'].pop('LastModified') - - result['Code']['Location'].should.equal('s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com/test.zip'.format(_lambda_region)) - result['Code']['RepositoryType'].should.equal('S3') - - result['Configuration']['CodeSha256'].should.equal(hashlib.sha256(zip_content).hexdigest()) - 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']['Handler'].should.equal('lambda_function.lambda_handler') - result['Configuration']['MemorySize'].should.equal(128) - result['Configuration']['Role'].should.equal('test-iam-role') - result['Configuration']['Runtime'].should.equal('python2.7') - result['Configuration']['Timeout'].should.equal(3) - result['Configuration']['Version'].should.equal('$LATEST') - result['Configuration'].should.contain('VpcConfig') - - # Test get function with - result = conn.get_function(FunctionName='testFunction', Qualifier='$LATEST') - result['Configuration']['Version'].should.equal('$LATEST') - - -@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_file2() - 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.lambda_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') - # this is hard to match against, so remove it - success_result['ResponseMetadata'].pop('HTTPHeaders', None) - # Botocore inserts retry attempts not seen in Python27 - success_result['ResponseMetadata'].pop('RetryAttempts', None) - - success_result.should.equal({'ResponseMetadata': {'HTTPStatusCode': 204}}) - - conn.delete_function.when.called_with( - FunctionName='testFunctionThatDoesntExist').should.throw(botocore.client.ClientError) - - -@mock_lambda -@mock_s3 -def test_publish(): - s3_conn = boto3.client('s3', 'us-west-2') - s3_conn.create_bucket(Bucket='test-bucket') - - zip_content = get_test_zip_file2() - 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.lambda_handler', - Code={ - 'S3Bucket': 'test-bucket', - 'S3Key': 'test.zip', - }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - - function_list = conn.list_functions() - function_list['Functions'].should.have.length_of(1) - latest_arn = function_list['Functions'][0]['FunctionArn'] - - conn.publish_version(FunctionName='testFunction') - - function_list = conn.list_functions() - function_list['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') - - conn.delete_function(FunctionName='testFunction', Qualifier='1') - - function_list = conn.list_functions() - function_list['Functions'].should.have.length_of(1) - function_list['Functions'][0]['FunctionArn'].should.contain('testFunction:$LATEST') - - - -@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_file2() - 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.lambda_handler', - Code={ - 'S3Bucket': 'test-bucket', - 'S3Key': 'test.zip', - }, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - expected_function_result = { - "Code": { - "Location": "s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com/test.zip".format(_lambda_region), - "RepositoryType": "S3" - }, - "Configuration": { - "CodeSha256": hashlib.sha256(zip_content).hexdigest(), - "CodeSize": len(zip_content), - "Description": "test lambda function", - "FunctionArn": 'arn:aws:lambda:{}:123456789012:function:testFunction:$LATEST'.format(_lambda_region), - "FunctionName": "testFunction", - "Handler": "lambda_function.lambda_handler", - "MemorySize": 128, - "Role": "test-iam-role", - "Runtime": "python2.7", - "Timeout": 3, - "Version": '$LATEST', - "VpcConfig": { - "SecurityGroupIds": [], - "SubnetIds": [], - } - }, - 'ResponseMetadata': {'HTTPStatusCode': 200}, - } - func = conn.list_functions()['Functions'][0] - func.pop('LastModified') - func.should.equal(expected_function_result['Configuration']) - - func = conn.get_function(FunctionName='testFunction') - # this is hard to match against, so remove it - func['ResponseMetadata'].pop('HTTPHeaders', None) - # Botocore inserts retry attempts not seen in Python27 - func['ResponseMetadata'].pop('RetryAttempts', None) - func['Configuration'].pop('LastModified') - - func.should.equal(expected_function_result) - conn.delete_function(FunctionName='testFunction') - - conn.list_functions()['Functions'].should.have.length_of(0) - - -@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='test-iam-role', - 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' - - -@mock_lambda -@mock_s3 -def test_tags(): - """ - test list_tags -> tag_resource -> list_tags -> tag_resource -> list_tags -> untag_resource -> list_tags integration - """ - s3_conn = boto3.client('s3', 'us-west-2') - s3_conn.create_bucket(Bucket='test-bucket') - - zip_content = get_test_zip_file2() - s3_conn.put_object(Bucket='test-bucket', Key='test.zip', Body=zip_content) - conn = boto3.client('lambda', 'us-west-2') - - function = 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, - ) - - # List tags when there are none - conn.list_tags( - Resource=function['FunctionArn'] - )['Tags'].should.equal(dict()) - - # List tags when there is one - conn.tag_resource( - Resource=function['FunctionArn'], - Tags=dict(spam='eggs') - )['ResponseMetadata']['HTTPStatusCode'].should.equal(200) - conn.list_tags( - Resource=function['FunctionArn'] - )['Tags'].should.equal(dict(spam='eggs')) - - # List tags when another has been added - conn.tag_resource( - Resource=function['FunctionArn'], - Tags=dict(foo='bar') - )['ResponseMetadata']['HTTPStatusCode'].should.equal(200) - conn.list_tags( - Resource=function['FunctionArn'] - )['Tags'].should.equal(dict(spam='eggs', foo='bar')) - - # Untag resource - conn.untag_resource( - Resource=function['FunctionArn'], - TagKeys=['spam', 'trolls'] - )['ResponseMetadata']['HTTPStatusCode'].should.equal(204) - conn.list_tags( - Resource=function['FunctionArn'] - )['Tags'].should.equal(dict(foo='bar')) - - # Untag a tag that does not exist (no error and no change) - conn.untag_resource( - Resource=function['FunctionArn'], - TagKeys=['spam'] - )['ResponseMetadata']['HTTPStatusCode'].should.equal(204) - - -@mock_lambda -def test_tags_not_found(): - """ - Test list_tags and tag_resource when the lambda with the given arn does not exist - """ - conn = boto3.client('lambda', 'us-west-2') - conn.list_tags.when.called_with( - Resource='arn:aws:lambda:123456789012:function:not-found' - ).should.throw(botocore.client.ClientError) - - conn.tag_resource.when.called_with( - Resource='arn:aws:lambda:123456789012:function:not-found', - Tags=dict(spam='eggs') - ).should.throw(botocore.client.ClientError) - - conn.untag_resource.when.called_with( - Resource='arn:aws:lambda:123456789012:function:not-found', - TagKeys=['spam'] - ).should.throw(botocore.client.ClientError) - - -@mock_lambda -def test_invoke_async_function(): - conn = boto3.client('lambda', 'us-west-2') - conn.create_function( - FunctionName='testFunction', - Runtime='python2.7', - Role='test-iam-role', - Handler='lambda_function.lambda_handler', - Code={'ZipFile': get_test_zip_file1()}, - Description='test lambda function', - Timeout=3, - MemorySize=128, - Publish=True, - ) - - success_result = conn.invoke_async( - FunctionName='testFunction', - 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', 'us-west-2') - zip_content = get_test_zip_file1() - 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, - ) - - response = conn.get_function( - FunctionName='testFunction' - ) - response['Configuration'].pop('LastModified') - - response['ResponseMetadata']['HTTPStatusCode'].should.equal(200) - assert len(response['Code']) == 2 - assert response['Code']['RepositoryType'] == 'S3' - assert response['Code']['Location'].startswith('s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com'.format(_lambda_region)) - response['Configuration'].should.equal( - { - "CodeSha256": hashlib.sha256(zip_content).hexdigest(), - "CodeSize": len(zip_content), - "Description": "test lambda function", - "FunctionArn":'arn:aws:lambda:{}:123456789012:function:testFunction:$LATEST'.format(_lambda_region), - "FunctionName": "testFunction", - "Handler": "lambda_function.handler", - "MemorySize": 128, - "Role": "test-iam-role", - "Runtime": "python2.7", - "Timeout": 3, - "Version": '$LATEST', - "VpcConfig": { - "SecurityGroupIds": [], - "SubnetIds": [], - } - }, - ) - - -@mock_lambda -def add_function_permission(): - conn = boto3.client('lambda', 'us-west-2') - zip_content = get_test_zip_file1() - 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, - ) - - response = conn.add_permission( - FunctionName='testFunction', - StatementId='1', - Action="lambda:InvokeFunction", - Principal='432143214321', - SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld", - SourceAccount='123412341234', - EventSourceToken='blah', - Qualifier='2' - ) - assert 'Statement' in response - res = json.loads(response['Statement']) - assert res['Action'] == "lambda:InvokeFunction" - - -@mock_lambda -def get_function_policy(): - conn = boto3.client('lambda', 'us-west-2') - zip_content = get_test_zip_file1() - 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, - ) - - response = conn.add_permission( - FunctionName='testFunction', - StatementId='1', - Action="lambda:InvokeFunction", - Principal='432143214321', - SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld", - SourceAccount='123412341234', - EventSourceToken='blah', - Qualifier='2' - ) - - response = conn.get_policy( - FunctionName='testFunction' - ) - - assert 'Policy' in response - assert isinstance(response['Policy'], str) - res = json.loads(response['Policy']) - assert res['Statement'][0]['Action'] == 'lambda:InvokeFunction' + assert result['IdentityId'] == '12345'