From 81836b698107debc9dffe056530820c9694c8c77 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Thu, 23 Feb 2017 19:43:48 -0500 Subject: [PATCH] Get standalone server mode working for all tests. --- CHANGELOG.md | 3 + Makefile | 3 + README.md | 35 +-- moto/apigateway/models.py | 4 +- moto/awslambda/models.py | 10 +- moto/awslambda/responses.py | 28 +- moto/awslambda/urls.py | 9 +- moto/backends.py | 124 ++++---- moto/cloudformation/responses.py | 7 +- moto/core/__init__.py | 2 + moto/core/exceptions.py | 6 + moto/core/models.py | 81 ++--- moto/core/responses.py | 59 +--- moto/core/utils.py | 10 +- moto/dynamodb/__init__.py | 2 + moto/dynamodb2/__init__.py | 2 + moto/ec2/responses/instances.py | 1 - moto/ec2/responses/ip_addresses.py | 1 - moto/ec2/responses/spot_instances.py | 6 +- moto/emr/exceptions.py | 7 + moto/emr/models.py | 1 + moto/emr/responses.py | 11 +- moto/events/__init__.py | 1 + moto/events/urls.py | 2 +- moto/iam/__init__.py | 2 + moto/iam/urls.py | 2 +- moto/instance_metadata/__init__.py | 4 + moto/instance_metadata/models.py | 7 + moto/instance_metadata/responses.py | 47 +++ moto/instance_metadata/urls.py | 12 + moto/kinesis/responses.py | 2 +- moto/route53/__init__.py | 2 + moto/route53/responses.py | 294 +++++++++--------- moto/route53/urls.py | 26 +- moto/s3/__init__.py | 2 + moto/s3/models.py | 10 +- moto/server.py | 15 +- moto/ses/__init__.py | 2 + moto/ses/urls.py | 3 +- moto/settings.py | 3 + moto/sts/__init__.py | 2 + moto/sts/urls.py | 2 +- other_langs/sqsSample.java | 52 ++++ other_langs/test.js | 26 ++ other_langs/test.rb | 6 + setup.cfg | 2 +- tests/test_apigateway/test_apigateway.py | 7 +- tests/test_awslambda/test_lambda.py | 23 +- .../test_cloudformation_stack_crud_boto3.py | 22 +- .../test_cloudformation_stack_integration.py | 44 +-- tests/test_core/test_instance_metadata.py | 21 +- tests/test_core/test_moto_api.py | 21 ++ tests/test_dynamodb/test_dynamodb.py | 7 - tests/test_dynamodb2/test_dynamodb.py | 8 - tests/test_ec2/test_amis.py | 22 +- tests/test_ec2/test_ec2_core.py | 10 - tests/test_ec2/test_elastic_block_store.py | 46 +-- tests/test_ec2/test_elastic_ip_addresses.py | 31 +- .../test_elastic_network_interfaces.py | 27 +- tests/test_ec2/test_instances.py | 54 ++-- tests/test_ec2/test_internet_gateways.py | 18 +- tests/test_ec2/test_key_pairs.py | 14 +- tests/test_ec2/test_security_groups.py | 38 +-- tests/test_ec2/test_spot_instances.py | 164 ++++++---- tests/test_ec2/test_tags.py | 14 +- tests/test_emr/test_emr.py | 12 +- tests/test_emr/test_emr_boto3.py | 16 +- tests/test_iam/test_iam.py | 4 +- tests/test_kinesis/test_firehose.py | 19 +- tests/test_route53/test_route53.py | 18 +- tests/test_s3/test_s3.py | 44 ++- .../test_s3bucket_path/test_s3bucket_path.py | 10 - tests/test_sns/test_publishing.py | 17 - tests/test_sns/test_publishing_boto3.py | 15 - tests/test_sqs/test_sqs.py | 5 - tests/test_sts/test_sts.py | 2 +- tests/test_swf/utils.py | 2 - tox.ini | 1 + 78 files changed, 934 insertions(+), 760 deletions(-) create mode 100644 moto/emr/exceptions.py create mode 100644 moto/instance_metadata/__init__.py create mode 100644 moto/instance_metadata/models.py create mode 100644 moto/instance_metadata/responses.py create mode 100644 moto/instance_metadata/urls.py create mode 100644 moto/settings.py create mode 100644 other_langs/sqsSample.java create mode 100644 other_langs/test.js create mode 100644 other_langs/test.rb create mode 100644 tests/test_core/test_moto_api.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 790f6de9..91265987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ Latest * The normal @mock_ decorators will no longer work with boto. It is suggested that you upgrade to boto3 or use the standalone-server mode. If you would still like to use boto, you must use the @mock__deprecated decorators which will be removed in a future release. * The @mock_s3bucket_path decorator is now deprecated. Use the @mock_s3 decorator instead. + Added + * Reset API: a reset API has been added to flush all of the current data ex: `requests.post("http://motoapi.amazonaws.com/moto-api/reset")` + 0.4.31 ------ diff --git a/Makefile b/Makefile index a7f08b14..58b74b2f 100644 --- a/Makefile +++ b/Makefile @@ -9,5 +9,8 @@ test: rm -rf cover @nosetests -sv --with-coverage --cover-html ./tests/ +test_server: + @TEST_SERVER_MODE=true nosetests -sv --with-coverage --cover-html ./tests/ + publish: python setup.py sdist bdist_wheel upload diff --git a/README.md b/README.md index ae161dc5..5485c63c 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,16 @@ -# Moto - Mock Boto +# Moto - Mock AWS Services [![Build Status](https://travis-ci.org/spulec/moto.png?branch=master)](https://travis-ci.org/spulec/moto) [![Coverage Status](https://coveralls.io/repos/spulec/moto/badge.png?branch=master)](https://coveralls.io/r/spulec/moto) # In a nutshell -Moto is a library that allows your python tests to easily mock out the boto library. +Moto is a library that allows your tests to easily mock out AWS Services. -Imagine you have the following code that you want to test: +Imagine you have the following python code that you want to test: ```python -import boto -from boto.s3.key import Key +import boto3 class MyModel(object): def __init__(self, name, value): @@ -19,11 +18,9 @@ class MyModel(object): self.value = value def save(self): - conn = boto.connect_s3() - bucket = conn.get_bucket('mybucket') - k = Key(bucket) - k.key = self.name - k.set_contents_from_string(self.value) + s3 = boto3.client('s3', region_name='us-east-1') + s3.put_object(Bucket='mybucket', Key=self.name, Body=self.value) + ``` Take a minute to think how you would have tested that in the past. @@ -31,25 +28,28 @@ Take a minute to think how you would have tested that in the past. Now see how you could test it with Moto: ```python -import boto +import boto3 from moto import mock_s3 from mymodule import MyModel + @mock_s3 def test_my_model_save(): - conn = boto.connect_s3() + conn = boto3.resource('s3', region_name='us-east-1') # We need to create the bucket since this is all in Moto's 'virtual' AWS account - conn.create_bucket('mybucket') + conn.create_bucket(Bucket='mybucket') model_instance = MyModel('steve', 'is awesome') model_instance.save() - assert conn.get_bucket('mybucket').get_key('steve').get_contents_as_string() == 'is awesome' + body = conn.Object('mybucket', 'steve').get()['Body'].read().decode("utf-8") + + assert body == b'is awesome' ``` With the decorator wrapping the test, all the calls to s3 are automatically mocked out. The mock keeps the state of the buckets and keys. -It gets even better! Moto isn't just S3. Here's the status of the other AWS services implemented. +It gets even better! Moto isn't just for Python code and it isn't just for S3. Look at the [standalone server mode](https://github.com/spulec/moto#stand-alone-server-mode) for more information about running Moto with other languages. Here's the status of the other AWS services implemented: ```gherkin |------------------------------------------------------------------------------| @@ -193,11 +193,6 @@ def test_my_model_save(): mock.stop() ``` -## Use with other libraries (boto3) or languages - -In general, Moto doesn't rely on anything specific to Boto. It only mocks AWS endpoints, so there should be no issue with boto3 or using other languages. Feel free to open an issue if something isn't working though. If you are using another language, you will need to either use the stand-alone server mode (more below) or monkey patch the HTTP calls yourself. - - ## Stand-alone Server Mode Moto also has a stand-alone server mode. This allows you to utilize diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py index b6fa2df0..4b09f44b 100644 --- a/moto/apigateway/models.py +++ b/moto/apigateway/models.py @@ -326,8 +326,8 @@ class RestAPI(object): return status_code, {}, response def update_integration_mocks(self, stage_name): - stage_url = STAGE_URL.format(api_id=self.id, region_name=self.region_name, stage_name=stage_name) - responses.add_callback(responses.GET, stage_url.lower(), callback=self.resource_callback) + stage_url = STAGE_URL.format(api_id=self.id.upper(), region_name=self.region_name, stage_name=stage_name) + responses.add_callback(responses.GET, stage_url, callback=self.resource_callback) def create_stage(self, name, deployment_id,variables=None,description='',cacheClusterEnabled=None,cacheClusterSize=None): if variables is None: diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py index e8595cc2..1fc139eb 100644 --- a/moto/awslambda/models.py +++ b/moto/awslambda/models.py @@ -154,15 +154,15 @@ class LambdaFunction(object): sys.stderr = sys.__stderr__ return self.convert(result) - def invoke(self, request, headers): + def invoke(self, body, request_headers, response_headers): payload = dict() # Get the invocation type: - r = self._invoke_lambda(code=self.code, event=request.body) - if request.headers.get("x-amz-invocation-type") == "RequestResponse": + r = self._invoke_lambda(code=self.code, event=body) + if request_headers.get("x-amz-invocation-type") == "RequestResponse": encoded = base64.b64encode(r.encode('utf-8')) - headers["x-amz-log-result"] = encoded.decode('utf-8') - payload['result'] = headers["x-amz-log-result"] + response_headers["x-amz-log-result"] = encoded.decode('utf-8') + payload['result'] = response_headers["x-amz-log-result"] result = r.encode('utf-8') else: result = json.dumps(payload) diff --git a/moto/awslambda/responses.py b/moto/awslambda/responses.py index 0cd7c57e..3fc756ef 100644 --- a/moto/awslambda/responses.py +++ b/moto/awslambda/responses.py @@ -10,32 +10,32 @@ from .models import lambda_backends class LambdaResponse(BaseResponse): - @classmethod - def root(cls, request, full_url, headers): + def root(self, request, full_url, headers): + self.setup_class(request, full_url, headers) if request.method == 'GET': - return cls()._list_functions(request, full_url, headers) + return self._list_functions(request, full_url, headers) elif request.method == 'POST': - return cls()._create_function(request, full_url, headers) + return self._create_function(request, full_url, headers) else: raise ValueError("Cannot handle request") - @classmethod - def function(cls, request, full_url, headers): + def function(self, request, full_url, headers): + self.setup_class(request, full_url, headers) if request.method == 'GET': - return cls()._get_function(request, full_url, headers) + return self._get_function(request, full_url, headers) elif request.method == 'DELETE': - return cls()._delete_function(request, full_url, headers) + return self._delete_function(request, full_url, headers) else: raise ValueError("Cannot handle request") - @classmethod - def invoke(cls, request, full_url, headers): + def invoke(self, request, full_url, headers): + self.setup_class(request, full_url, headers) if request.method == 'POST': - return cls()._invoke(request, full_url, headers) + return self._invoke(request, full_url) else: raise ValueError("Cannot handle request") - def _invoke(self, request, full_url, headers): + def _invoke(self, request, full_url): response_headers = {} lambda_backend = self.get_lambda_backend(full_url) @@ -44,7 +44,7 @@ class LambdaResponse(BaseResponse): if lambda_backend.has_function(function_name): fn = lambda_backend.get_function(function_name) - payload = fn.invoke(request, response_headers) + payload = fn.invoke(self.body, self.headers, response_headers) response_headers['Content-Length'] = str(len(payload)) return 202, response_headers, payload else: @@ -59,7 +59,7 @@ class LambdaResponse(BaseResponse): def _create_function(self, request, full_url, headers): lambda_backend = self.get_lambda_backend(full_url) - spec = json.loads(request.body.decode('utf-8')) + spec = json.loads(self.body.decode('utf-8')) try: fn = lambda_backend.create_function(spec) except ValueError as e: diff --git a/moto/awslambda/urls.py b/moto/awslambda/urls.py index 79a99c9f..c6313576 100644 --- a/moto/awslambda/urls.py +++ b/moto/awslambda/urls.py @@ -5,9 +5,10 @@ url_bases = [ "https?://lambda.(.+).amazonaws.com", ] +response = LambdaResponse() + 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, + '{0}/(?P[^/]+)/functions/?$': response.root, + '{0}/(?P[^/]+)/functions/(?P[\w_-]+)/?$': response.function, + '{0}/(?P[^/]+)/functions/(?P[\w_-]+)/invocations/?$': response.invoke, } diff --git a/moto/backends.py b/moto/backends.py index 4cebe560..5b1695e3 100644 --- a/moto/backends.py +++ b/moto/backends.py @@ -1,67 +1,71 @@ from __future__ import unicode_literals -from moto.apigateway import apigateway_backend -from moto.autoscaling import autoscaling_backend -from moto.awslambda import lambda_backend -from moto.cloudformation import cloudformation_backend -from moto.cloudwatch import cloudwatch_backend -from moto.core import moto_api_backend -from moto.datapipeline import datapipeline_backend -from moto.dynamodb import dynamodb_backend -from moto.dynamodb2 import dynamodb_backend2 -from moto.ec2 import ec2_backend -from moto.ecs import ecs_backend -from moto.elb import elb_backend -from moto.emr import emr_backend -from moto.events import events_backend -from moto.glacier import glacier_backend -from moto.iam import iam_backend -from moto.kinesis import kinesis_backend -from moto.kms import kms_backend -from moto.opsworks import opsworks_backend -from moto.rds import rds_backend -from moto.redshift import redshift_backend -from moto.route53 import route53_backend -from moto.s3 import s3_backend -from moto.ses import ses_backend -from moto.sns import sns_backend -from moto.sqs import sqs_backend -from moto.sts import sts_backend +from moto.apigateway import apigateway_backends +from moto.autoscaling import autoscaling_backends +from moto.awslambda import lambda_backends +from moto.cloudformation import cloudformation_backends +from moto.cloudwatch import cloudwatch_backends +from moto.core import moto_api_backends +from moto.datapipeline import datapipeline_backends +from moto.dynamodb import dynamodb_backends +from moto.dynamodb2 import dynamodb_backends2 +from moto.ec2 import ec2_backends +from moto.ecs import ecs_backends +from moto.elb import elb_backends +from moto.emr import emr_backends +from moto.events import events_backends +from moto.glacier import glacier_backends +from moto.iam import iam_backends +from moto.instance_metadata import instance_metadata_backends +from moto.kinesis import kinesis_backends +from moto.kms import kms_backends +from moto.opsworks import opsworks_backends +from moto.rds2 import rds2_backends +from moto.redshift import redshift_backends +from moto.route53 import route53_backends +from moto.s3 import s3_backends +from moto.ses import ses_backends +from moto.sns import sns_backends +from moto.sqs import sqs_backends +from moto.sts import sts_backends BACKENDS = { - 'apigateway': apigateway_backend, - 'autoscaling': autoscaling_backend, - 'cloudformation': cloudformation_backend, - 'cloudwatch': cloudwatch_backend, - 'datapipeline': datapipeline_backend, - 'dynamodb': dynamodb_backend, - 'dynamodb2': dynamodb_backend2, - 'ec2': ec2_backend, - 'ecs': ecs_backend, - 'elb': elb_backend, - 'events': events_backend, - 'emr': emr_backend, - 'glacier': glacier_backend, - 'iam': iam_backend, - 'moto_api': moto_api_backend, - 'opsworks': opsworks_backend, - 'kinesis': kinesis_backend, - 'kms': kms_backend, - 'redshift': redshift_backend, - 'rds': rds_backend, - 's3': s3_backend, - 's3bucket_path': s3_backend, - 'ses': ses_backend, - 'sns': sns_backend, - 'sqs': sqs_backend, - 'sts': sts_backend, - 'route53': route53_backend, - 'lambda': lambda_backend, + 'apigateway': apigateway_backends, + 'autoscaling': autoscaling_backends, + 'cloudformation': cloudformation_backends, + 'cloudwatch': cloudwatch_backends, + 'datapipeline': datapipeline_backends, + 'dynamodb': dynamodb_backends, + 'dynamodb2': dynamodb_backends2, + 'ec2': ec2_backends, + 'ecs': ecs_backends, + 'elb': elb_backends, + 'events': events_backends, + 'emr': emr_backends, + 'glacier': glacier_backends, + 'iam': iam_backends, + 'moto_api': moto_api_backends, + 'instance_metadata': instance_metadata_backends, + 'opsworks': opsworks_backends, + 'kinesis': kinesis_backends, + 'kms': kms_backends, + 'redshift': redshift_backends, + 'rds': rds2_backends, + 's3': s3_backends, + 's3bucket_path': s3_backends, + 'ses': ses_backends, + 'sns': sns_backends, + 'sqs': sqs_backends, + 'sts': sts_backends, + 'route53': route53_backends, + 'lambda': lambda_backends, } -def get_model(name): - for backend in BACKENDS.values(): - models = getattr(backend.__class__, '__models__', {}) - if name in models: - return list(getattr(backend, models[name])()) +def get_model(name, region): + for backends in BACKENDS.values(): + for region, backend in backends.items(): + if region == region: + models = getattr(backend.__class__, '__models__', {}) + if name in models: + return list(getattr(backend, models[name])()) diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index d16b3560..3b8f5389 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -17,8 +17,11 @@ class CloudFormationResponse(BaseResponse): def _get_stack_from_s3_url(self, template_url): template_url_parts = urlparse(template_url) - bucket_name = template_url_parts.netloc.split(".")[0] - key_name = template_url_parts.path.lstrip("/") + if "localhost" in template_url: + bucket_name, key_name = template_url_parts.path.lstrip("/").split("/") + else: + bucket_name = template_url_parts.netloc.split(".")[0] + key_name = template_url_parts.path.lstrip("/") key = s3_backend.get_key(bucket_name, key_name) return key.value.decode("utf-8") diff --git a/moto/core/__init__.py b/moto/core/__init__.py index 664637b7..4f783d46 100644 --- a/moto/core/__init__.py +++ b/moto/core/__init__.py @@ -1,2 +1,4 @@ from __future__ import unicode_literals from .models import BaseBackend, moto_api_backend # flake8: noqa + +moto_api_backends = {"global": moto_api_backend} diff --git a/moto/core/exceptions.py b/moto/core/exceptions.py index c66b8f25..d3a87e29 100644 --- a/moto/core/exceptions.py +++ b/moto/core/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from werkzeug.exceptions import HTTPException from jinja2 import DictLoader, Environment from six import text_type @@ -47,6 +49,10 @@ class RESTError(HTTPException): error_type=error_type, message=message, **kwargs) +class DryRunClientError(RESTError): + code = 400 + + class JsonRESTError(RESTError): def __init__(self, error_type, message, template='error_json', **kwargs): super(JsonRESTError, self).__init__(error_type, message, template, **kwargs) diff --git a/moto/core/models.py b/moto/core/models.py index 8fac8a99..04ff709e 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -6,9 +6,9 @@ import inspect import os import re +from moto import settings from moto.packages.responses import responses from moto.packages.httpretty import HTTPretty -from .responses import metadata_response from .utils import ( convert_httpretty_response, convert_regex_to_flask_path, @@ -21,6 +21,15 @@ class BaseMockAWS(object): def __init__(self, backends): self.backends = backends + self.backends_for_urls = {} + from moto.backends import BACKENDS + default_backends = { + "instance_metadata": BACKENDS['instance_metadata']['global'], + "moto_api": BACKENDS['moto_api']['global'], + } + self.backends_for_urls.update(self.backends) + self.backends_for_urls.update(default_backends) + if self.__class__.nested_count == 0: self.reset() @@ -95,20 +104,13 @@ class HttprettyMockAWS(BaseMockAWS): HTTPretty.enable() for method in HTTPretty.METHODS: - backend = list(self.backends.values())[0] - for key, value in backend.urls.items(): - HTTPretty.register_uri( - method=method, - uri=re.compile(key), - body=convert_httpretty_response(value), - ) - - # Mock out localhost instance metadata - HTTPretty.register_uri( - method=method, - uri=re.compile('http://169.254.169.254/latest/meta-data/.*'), - body=convert_httpretty_response(metadata_response), - ) + for backend in self.backends_for_urls.values(): + for key, value in backend.urls.items(): + HTTPretty.register_uri( + method=method, + uri=re.compile(key), + body=convert_httpretty_response(value), + ) def disable_patching(self): HTTPretty.disable() @@ -126,20 +128,14 @@ class ResponsesMockAWS(BaseMockAWS): def enable_patching(self): responses.start() for method in RESPONSES_METHODS: - backend = list(self.backends.values())[0] - for key, value in backend.urls.items(): - responses.add_callback( - method=method, - url=re.compile(key), - callback=convert_flask_to_responses_response(value), - ) + for backend in self.backends_for_urls.values(): + for key, value in backend.urls.items(): + responses.add_callback( + method=method, + url=re.compile(key), + callback=convert_flask_to_responses_response(value), + ) - # Mock out localhost instance metadata - responses.add_callback( - method=method, - url=re.compile('http://169.254.169.254/latest/meta-data/.*'), - callback=convert_flask_to_responses_response(metadata_response), - ) for pattern in responses.mock._urls: pattern['stream'] = True @@ -270,10 +266,15 @@ class BaseBackend(object): return paths def decorator(self, func=None): - if func: - return MockAWS({'global': self})(func) + if settings.TEST_SERVER_MODE: + mocked_backend = ServerModeMockAWS({'global': self}) else: - return MockAWS({'global': self}) + mocked_backend = MockAWS({'global': self}) + + if func: + return mocked_backend(func) + else: + return mocked_backend def deprecated_decorator(self, func=None): if func: @@ -289,13 +290,15 @@ class base_decorator(object): self.backends = backends def __call__(self, func=None): - if self.mock_backend == MockAWS and os.environ.get('TEST_SERVER_MODE', '0').lower() == 'true': - self.mock_backend = ServerModeMockAWS + if self.mock_backend != HttprettyMockAWS and settings.TEST_SERVER_MODE: + mocked_backend = ServerModeMockAWS(self.backends) + else: + mocked_backend = self.mock_backend(self.backends) if func: - return self.mock_backend(self.backends)(func) + return mocked_backend(func) else: - return self.mock_backend(self.backends) + return mocked_backend class deprecated_base_decorator(base_decorator): @@ -303,15 +306,13 @@ class deprecated_base_decorator(base_decorator): class MotoAPIBackend(BaseBackend): - def __init__(self): - super(MotoAPIBackend, self).__init__() - def reset(self): from moto.backends import BACKENDS - for name, backend in BACKENDS.items(): + for name, backends in BACKENDS.items(): if name == "moto_api": continue - backend.reset() + for region_name, backend in backends.items(): + backend.reset() self.__init__() moto_api_backend = MotoAPIBackend() diff --git a/moto/core/responses.py b/moto/core/responses.py index 9b22b58c..e558eb1d 100644 --- a/moto/core/responses.py +++ b/moto/core/responses.py @@ -5,7 +5,7 @@ import logging import re import pytz -from boto.exception import JSONResponseError +from moto.core.exceptions import DryRunClientError from jinja2 import Environment, DictLoader, TemplateNotFound @@ -149,17 +149,19 @@ class BaseResponse(_TemplateEnvironmentMixin): self.path = urlparse(full_url).path self.querystring = querystring self.method = request.method - self.region = self.get_region_from_url(full_url) + self.region = self.get_region_from_url(request, full_url) self.headers = request.headers if 'host' not in self.headers: self.headers['host'] = urlparse(full_url).netloc self.response_headers = {"server": "amazon.com"} - def get_region_from_url(self, full_url): + def get_region_from_url(self, request, full_url): match = re.search(self.region_regex, full_url) if match: region = match.group(1) + elif 'Authorization' in request.headers: + region = request.headers['Authorization'].split(",")[0].split("/")[2] else: region = self.default_region return region @@ -195,6 +197,7 @@ class BaseResponse(_TemplateEnvironmentMixin): if "status" in headers: headers['status'] = str(headers['status']) return status, headers, body + raise NotImplementedError("The {0} action has not been implemented".format(action)) def _get_param(self, param_name, if_none=None): @@ -323,55 +326,19 @@ class BaseResponse(_TemplateEnvironmentMixin): def is_not_dryrun(self, action): if 'true' in self.querystring.get('DryRun', ['false']): - raise JSONResponseError(400, 'DryRunOperation', body={'message': 'An error occurred (DryRunOperation) when calling the %s operation: Request would have succeeded, but DryRun flag is set' % action}) + message = 'An error occurred (DryRunOperation) when calling the %s operation: Request would have succeeded, but DryRun flag is set' % action + raise DryRunClientError(error_type="DryRunOperation", message=message) return True -def metadata_response(request, full_url, headers): - """ - Mock response for localhost metadata - - http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html - """ - - parsed_url = urlparse(full_url) - tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) - credentials = dict( - AccessKeyId="test-key", - SecretAccessKey="test-secret-key", - Token="test-session-token", - Expiration=tomorrow.strftime("%Y-%m-%dT%H:%M:%SZ") - ) - - path = parsed_url.path - - meta_data_prefix = "/latest/meta-data/" - # Strip prefix if it is there - if path.startswith(meta_data_prefix): - path = path[len(meta_data_prefix):] - - if path == '': - result = 'iam' - elif path == 'iam': - result = json.dumps({ - 'security-credentials': { - 'default-role': credentials - } - }) - elif path == 'iam/security-credentials/': - result = 'default-role' - elif path == 'iam/security-credentials/default-role': - result = json.dumps(credentials) - else: - raise NotImplementedError("The {0} metadata path has not been implemented".format(path)) - return 200, headers, result - class MotoAPIResponse(BaseResponse): def reset_response(self, request, full_url, headers): - from .models import moto_api_backend - moto_api_backend.reset() - return 200, {}, json.dumps({"status": "ok"}) + if request.method == "POST": + from .models import moto_api_backend + moto_api_backend.reset() + return 200, {}, json.dumps({"status": "ok"}) + return 400, {}, json.dumps({"Error": "Need to POST to reset Moto"}) class _RecursiveDictRef(object): diff --git a/moto/core/utils.py b/moto/core/utils.py index 451d1a76..11aafbb8 100644 --- a/moto/core/utils.py +++ b/moto/core/utils.py @@ -118,12 +118,16 @@ class convert_flask_to_httpretty_response(object): return "{0}.{1}".format(outer, self.callback.__name__) def __call__(self, args=None, **kwargs): - from flask import request + from flask import request, Response result = self.callback(request, request.url, {}) # result is a status, headers, response tuple - status, headers, response = result - return response, status, headers + status, headers, content = result + + response = Response(response=content, status=status, headers=headers) + if request.method == "HEAD" and 'content-length' in headers: + response.headers['Content-Length'] = headers['content-length'] + return response class convert_flask_to_responses_response(object): diff --git a/moto/dynamodb/__init__.py b/moto/dynamodb/__init__.py index 00805031..4c2bc04d 100644 --- a/moto/dynamodb/__init__.py +++ b/moto/dynamodb/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import dynamodb_backend + +dynamodb_backends = {"global": dynamodb_backend} mock_dynamodb = dynamodb_backend.decorator mock_dynamodb_deprecated = dynamodb_backend.deprecated_decorator diff --git a/moto/dynamodb2/__init__.py b/moto/dynamodb2/__init__.py index f0892d13..7a1f0735 100644 --- a/moto/dynamodb2/__init__.py +++ b/moto/dynamodb2/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import dynamodb_backend2 + +dynamodb_backends2 = {"global": dynamodb_backend2} mock_dynamodb2 = dynamodb_backend2.decorator mock_dynamodb2_deprecated = dynamodb_backend2.deprecated_decorator \ No newline at end of file diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py index 10cdcd07..3c5a087d 100644 --- a/moto/ec2/responses/instances.py +++ b/moto/ec2/responses/instances.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals from boto.ec2.instancetype import InstanceType -from boto.exception import JSONResponseError from moto.core.responses import BaseResponse from moto.core.utils import camelcase_to_underscores from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, \ diff --git a/moto/ec2/responses/ip_addresses.py b/moto/ec2/responses/ip_addresses.py index fd58741e..99571920 100644 --- a/moto/ec2/responses/ip_addresses.py +++ b/moto/ec2/responses/ip_addresses.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -from boto.exception import JSONResponseError from moto.core.responses import BaseResponse diff --git a/moto/ec2/responses/spot_instances.py b/moto/ec2/responses/spot_instances.py index 321ecd99..96e5a1ba 100644 --- a/moto/ec2/responses/spot_instances.py +++ b/moto/ec2/responses/spot_instances.py @@ -35,8 +35,8 @@ class SpotInstances(BaseResponse): def request_spot_instances(self): price = self._get_param('SpotPrice') image_id = self._get_param('LaunchSpecification.ImageId') - count = self._get_int_param('InstanceCount') - type = self._get_param('Type') + count = self._get_int_param('InstanceCount', 1) + type = self._get_param('Type', 'one-time') valid_from = self._get_param('ValidFrom') valid_until = self._get_param('ValidUntil') launch_group = self._get_param('LaunchGroup') @@ -44,7 +44,7 @@ class SpotInstances(BaseResponse): key_name = self._get_param('LaunchSpecification.KeyName') security_groups = self._get_multi_param('LaunchSpecification.SecurityGroup') user_data = self._get_param('LaunchSpecification.UserData') - instance_type = self._get_param('LaunchSpecification.InstanceType') + instance_type = self._get_param('LaunchSpecification.InstanceType', 'm1.small') placement = self._get_param('LaunchSpecification.Placement.AvailabilityZone') kernel_id = self._get_param('LaunchSpecification.KernelId') ramdisk_id = self._get_param('LaunchSpecification.RamdiskId') diff --git a/moto/emr/exceptions.py b/moto/emr/exceptions.py new file mode 100644 index 00000000..1a3398d4 --- /dev/null +++ b/moto/emr/exceptions.py @@ -0,0 +1,7 @@ +from __future__ import unicode_literals + +from moto.core.exceptions import RESTError + + +class EmrError(RESTError): + code = 400 diff --git a/moto/emr/models.py b/moto/emr/models.py index f9242833..155e4a89 100644 --- a/moto/emr/models.py +++ b/moto/emr/models.py @@ -299,6 +299,7 @@ class ElasticMapReduceBackend(BaseBackend): created_before = dtparse(created_before) clusters = [c for c in clusters if c.creation_datetime < created_before] + # Amazon EMR can return a maximum of 512 job flow descriptions return sorted(clusters, key=lambda x: x.id)[:512] def describe_step(self, cluster_id, step_id): diff --git a/moto/emr/responses.py b/moto/emr/responses.py index 427ab48c..3869c33f 100644 --- a/moto/emr/responses.py +++ b/moto/emr/responses.py @@ -5,15 +5,14 @@ from datetime import datetime from functools import wraps import pytz -from botocore.exceptions import ClientError from moto.compat import urlparse from moto.core.responses import AWSServiceSpec from moto.core.responses import BaseResponse from moto.core.responses import xml_to_json_response +from .exceptions import EmrError from .models import emr_backends -from .utils import steps_from_query_string -from .utils import tags_from_query_string +from .utils import steps_from_query_string, tags_from_query_string def generate_boto3_response(operation): @@ -46,7 +45,7 @@ class ElasticMapReduceResponse(BaseResponse): aws_service_spec = AWSServiceSpec('data/emr/2009-03-31/service-2.json') - def get_region_from_url(self, full_url): + def get_region_from_url(self, request, full_url): parsed = urlparse(full_url) for regex in self.region_regex: match = regex.search(parsed.netloc) @@ -240,9 +239,7 @@ class ElasticMapReduceResponse(BaseResponse): 'Only one AMI version and release label may be specified. ' 'Provided AMI: {0}, release label: {1}.').format( ami_version, release_label) - raise ClientError( - {'Error': {'Code': 'ValidationException', - 'Message': message}}, 'RunJobFlow') + raise EmrError(error_type="ValidationException", message=message, template='single_error') else: if ami_version: kwargs['requested_ami_version'] = ami_version diff --git a/moto/events/__init__.py b/moto/events/__init__.py index 8b15e852..5c93c59c 100644 --- a/moto/events/__init__.py +++ b/moto/events/__init__.py @@ -2,4 +2,5 @@ from __future__ import unicode_literals from .models import events_backend +events_backends = {"global": events_backend} mock_events = events_backend.decorator diff --git a/moto/events/urls.py b/moto/events/urls.py index bff05da3..a6e533b0 100644 --- a/moto/events/urls.py +++ b/moto/events/urls.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from .responses import EventsHandler url_bases = [ - "https://events.(.+).amazonaws.com" + "https?://events.(.+).amazonaws.com" ] url_paths = { diff --git a/moto/iam/__init__.py b/moto/iam/__init__.py index 02519cbc..c5110b35 100644 --- a/moto/iam/__init__.py +++ b/moto/iam/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import iam_backend + +iam_backends = {"global": iam_backend} mock_iam = iam_backend.decorator mock_iam_deprecated = iam_backend.deprecated_decorator \ No newline at end of file diff --git a/moto/iam/urls.py b/moto/iam/urls.py index a591e3eb..46db41e4 100644 --- a/moto/iam/urls.py +++ b/moto/iam/urls.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from .responses import IamResponse url_bases = [ - "https?://iam.amazonaws.com", + "https?://iam(.*).amazonaws.com", ] url_paths = { diff --git a/moto/instance_metadata/__init__.py b/moto/instance_metadata/__init__.py new file mode 100644 index 00000000..9197bcf7 --- /dev/null +++ b/moto/instance_metadata/__init__.py @@ -0,0 +1,4 @@ +from __future__ import unicode_literals +from .models import instance_metadata_backend + +instance_metadata_backends = {"global": instance_metadata_backend} \ No newline at end of file diff --git a/moto/instance_metadata/models.py b/moto/instance_metadata/models.py new file mode 100644 index 00000000..b86f8637 --- /dev/null +++ b/moto/instance_metadata/models.py @@ -0,0 +1,7 @@ +from moto.core.models import BaseBackend + + +class InstanceMetadataBackend(BaseBackend): + pass + +instance_metadata_backend = InstanceMetadataBackend() diff --git a/moto/instance_metadata/responses.py b/moto/instance_metadata/responses.py new file mode 100644 index 00000000..b2de66e7 --- /dev/null +++ b/moto/instance_metadata/responses.py @@ -0,0 +1,47 @@ +from __future__ import unicode_literals +import datetime +import json +from urlparse import urlparse + +from moto.core.responses import BaseResponse + + +class InstanceMetadataResponse(BaseResponse): + def metadata_response(self, request, full_url, headers): + """ + Mock response for localhost metadata + + http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html + """ + + parsed_url = urlparse(full_url) + tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) + credentials = dict( + AccessKeyId="test-key", + SecretAccessKey="test-secret-key", + Token="test-session-token", + Expiration=tomorrow.strftime("%Y-%m-%dT%H:%M:%SZ") + ) + + path = parsed_url.path + + meta_data_prefix = "/latest/meta-data/" + # Strip prefix if it is there + if path.startswith(meta_data_prefix): + path = path[len(meta_data_prefix):] + + if path == '': + result = 'iam' + elif path == 'iam': + result = json.dumps({ + 'security-credentials': { + 'default-role': credentials + } + }) + elif path == 'iam/security-credentials/': + result = 'default-role' + elif path == 'iam/security-credentials/default-role': + result = json.dumps(credentials) + else: + raise NotImplementedError("The {0} metadata path has not been implemented".format(path)) + return 200, headers, result diff --git a/moto/instance_metadata/urls.py b/moto/instance_metadata/urls.py new file mode 100644 index 00000000..7776b364 --- /dev/null +++ b/moto/instance_metadata/urls.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from .responses import InstanceMetadataResponse + +url_bases = [ + "http://169.254.169.254" +] + +instance_metadata = InstanceMetadataResponse() + +url_paths = { + '{0}/(?P.+)': instance_metadata.metadata_response, +} diff --git a/moto/kinesis/responses.py b/moto/kinesis/responses.py index 9aed719d..29f6c07f 100644 --- a/moto/kinesis/responses.py +++ b/moto/kinesis/responses.py @@ -20,7 +20,7 @@ class KinesisResponse(BaseResponse): @property def is_firehose(self): host = self.headers.get('host') or self.headers['Host'] - return host.startswith('firehose') + return host.startswith('firehose') or 'firehose' in self.headers.get('Authorization', '') def create_stream(self): stream_name = self.parameters.get('StreamName') diff --git a/moto/route53/__init__.py b/moto/route53/__init__.py index df629880..e2bbe4c1 100644 --- a/moto/route53/__init__.py +++ b/moto/route53/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import route53_backend + +route53_backends = {"global": route53_backend} mock_route53 = route53_backend.decorator mock_route53_deprecated = route53_backend.deprecated_decorator diff --git a/moto/route53/responses.py b/moto/route53/responses.py index 429317da..d796660e 100644 --- a/moto/route53/responses.py +++ b/moto/route53/responses.py @@ -1,174 +1,186 @@ from __future__ import unicode_literals from jinja2 import Template from six.moves.urllib.parse import parse_qs, urlparse + +from moto.core.responses import BaseResponse from .models import route53_backend import xmltodict -def list_or_create_hostzone_response(request, full_url, headers): +class Route53 (BaseResponse): + def list_or_create_hostzone_response(self, request, full_url, headers): + self.setup_class(request, full_url, headers) - if request.method == "POST": - elements = xmltodict.parse(request.body) - if "HostedZoneConfig" in elements["CreateHostedZoneRequest"]: - comment = elements["CreateHostedZoneRequest"]["HostedZoneConfig"]["Comment"] - try: - # in boto3, this field is set directly in the xml - private_zone = elements["CreateHostedZoneRequest"]["HostedZoneConfig"]["PrivateZone"] - except KeyError: - # if a VPC subsection is only included in xmls params when private_zone=True, - # see boto: boto/route53/connection.py - private_zone = 'VPC' in elements["CreateHostedZoneRequest"] - else: - comment = None - private_zone = False + if request.method == "POST": + elements = xmltodict.parse(self.body) + if "HostedZoneConfig" in elements["CreateHostedZoneRequest"]: + comment = elements["CreateHostedZoneRequest"]["HostedZoneConfig"]["Comment"] + try: + # in boto3, this field is set directly in the xml + private_zone = elements["CreateHostedZoneRequest"]["HostedZoneConfig"]["PrivateZone"] + except KeyError: + # if a VPC subsection is only included in xmls params when private_zone=True, + # see boto: boto/route53/connection.py + private_zone = 'VPC' in elements["CreateHostedZoneRequest"] + else: + comment = None + private_zone = False + + name = elements["CreateHostedZoneRequest"]["Name"] + + if name[-1] != ".": + name += "." + + new_zone = route53_backend.create_hosted_zone( + name, + comment=comment, + private_zone=private_zone, + ) + template = Template(CREATE_HOSTED_ZONE_RESPONSE) + return 201, headers, template.render(zone=new_zone) + + elif request.method == "GET": + all_zones = route53_backend.get_all_hosted_zones() + template = Template(LIST_HOSTED_ZONES_RESPONSE) + return 200, headers, template.render(zones=all_zones) - name = elements["CreateHostedZoneRequest"]["Name"] + def get_or_delete_hostzone_response(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + parsed_url = urlparse(full_url) + zoneid = parsed_url.path.rstrip('/').rsplit('/', 1)[1] + the_zone = route53_backend.get_hosted_zone(zoneid) + if not the_zone: + return 404, headers, "Zone %s not Found" % zoneid - if name[-1] != ".": - name += "." + if request.method == "GET": + template = Template(GET_HOSTED_ZONE_RESPONSE) - new_zone = route53_backend.create_hosted_zone( - name, - comment=comment, - private_zone=private_zone, - ) - template = Template(CREATE_HOSTED_ZONE_RESPONSE) - return 201, headers, template.render(zone=new_zone) - - elif request.method == "GET": - all_zones = route53_backend.get_all_hosted_zones() - template = Template(LIST_HOSTED_ZONES_RESPONSE) - return 200, headers, template.render(zones=all_zones) + return 200, headers, template.render(zone=the_zone) + elif request.method == "DELETE": + route53_backend.delete_hosted_zone(zoneid) + return 200, headers, DELETE_HOSTED_ZONE_RESPONSE -def get_or_delete_hostzone_response(request, full_url, headers): - parsed_url = urlparse(full_url) - zoneid = parsed_url.path.rstrip('/').rsplit('/', 1)[1] - the_zone = route53_backend.get_hosted_zone(zoneid) - if not the_zone: - return 404, headers, "Zone %s not Found" % zoneid + def rrset_response(self, request, full_url, headers): + self.setup_class(request, full_url, headers) - if request.method == "GET": - template = Template(GET_HOSTED_ZONE_RESPONSE) + parsed_url = urlparse(full_url) + method = request.method - return 200, headers, template.render(zone=the_zone) - elif request.method == "DELETE": - route53_backend.delete_hosted_zone(zoneid) - return 200, headers, DELETE_HOSTED_ZONE_RESPONSE + zoneid = parsed_url.path.rstrip('/').rsplit('/', 2)[1] + the_zone = route53_backend.get_hosted_zone(zoneid) + if not the_zone: + return 404, headers, "Zone %s Not Found" % zoneid + + if method == "POST": + elements = xmltodict.parse(self.body) + + change_list = elements['ChangeResourceRecordSetsRequest']['ChangeBatch']['Changes']['Change'] + if not isinstance(change_list, list): + change_list = [elements['ChangeResourceRecordSetsRequest']['ChangeBatch']['Changes']['Change']] + + for value in change_list: + action = value['Action'] + record_set = value['ResourceRecordSet'] + if action in ('CREATE', 'UPSERT'): + if 'ResourceRecords' in record_set: + resource_records = list(record_set['ResourceRecords'].values())[0] + if not isinstance(resource_records, list): + # Depending on how many records there are, this may or may not be a list + resource_records = [resource_records] + record_values = [x['Value'] for x in resource_records] + elif 'AliasTarget' in record_set: + record_values = [record_set['AliasTarget']['DNSName']] + record_set['ResourceRecords'] = record_values + if action == 'CREATE': + the_zone.add_rrset(record_set) + else: + the_zone.upsert_rrset(record_set) + elif action == "DELETE": + if 'SetIdentifier' in record_set: + the_zone.delete_rrset_by_id(record_set["SetIdentifier"]) + else: + the_zone.delete_rrset_by_name(record_set["Name"]) + + return 200, headers, CHANGE_RRSET_RESPONSE + + elif method == "GET": + querystring = parse_qs(parsed_url.query) + template = Template(LIST_RRSET_REPONSE) + type_filter = querystring.get("type", [None])[0] + name_filter = querystring.get("name", [None])[0] + record_sets = the_zone.get_record_sets(type_filter, name_filter) + return 200, headers, template.render(record_sets=record_sets) -def rrset_response(request, full_url, headers): - parsed_url = urlparse(full_url) - method = request.method + def health_check_response(self, request, full_url, headers): + self.setup_class(request, full_url, headers) - zoneid = parsed_url.path.rstrip('/').rsplit('/', 2)[1] - the_zone = route53_backend.get_hosted_zone(zoneid) - if not the_zone: - return 404, headers, "Zone %s Not Found" % zoneid + parsed_url = urlparse(full_url) + method = request.method - if method == "POST": - elements = xmltodict.parse(request.body) + if method == "POST": + properties = xmltodict.parse(self.body)['CreateHealthCheckRequest']['HealthCheckConfig'] + health_check_args = { + "ip_address": properties.get('IPAddress'), + "port": properties.get('Port'), + "type": properties['Type'], + "resource_path": properties.get('ResourcePath'), + "fqdn": properties.get('FullyQualifiedDomainName'), + "search_string": properties.get('SearchString'), + "request_interval": properties.get('RequestInterval'), + "failure_threshold": properties.get('FailureThreshold'), + } + health_check = route53_backend.create_health_check(health_check_args) + template = Template(CREATE_HEALTH_CHECK_RESPONSE) + return 201, headers, template.render(health_check=health_check) + elif method == "DELETE": + health_check_id = parsed_url.path.split("/")[-1] + route53_backend.delete_health_check(health_check_id) + return 200, headers, DELETE_HEALTH_CHECK_REPONSE + elif method == "GET": + template = Template(LIST_HEALTH_CHECKS_REPONSE) + health_checks = route53_backend.get_health_checks() + return 200, headers, template.render(health_checks=health_checks) - change_list = elements['ChangeResourceRecordSetsRequest']['ChangeBatch']['Changes']['Change'] - if not isinstance(change_list, list): - change_list = [elements['ChangeResourceRecordSetsRequest']['ChangeBatch']['Changes']['Change']] + def not_implemented_response(self, request, full_url, headers): + self.setup_class(request, full_url, headers) - for value in change_list: - action = value['Action'] - record_set = value['ResourceRecordSet'] - if action in ('CREATE', 'UPSERT'): - if 'ResourceRecords' in record_set: - resource_records = list(record_set['ResourceRecords'].values())[0] - if not isinstance(resource_records, list): - # Depending on how many records there are, this may or may not be a list - resource_records = [resource_records] - record_values = [x['Value'] for x in resource_records] - elif 'AliasTarget' in record_set: - record_values = [record_set['AliasTarget']['DNSName']] - record_set['ResourceRecords'] = record_values - if action == 'CREATE': - the_zone.add_rrset(record_set) - else: - the_zone.upsert_rrset(record_set) - elif action == "DELETE": - if 'SetIdentifier' in record_set: - the_zone.delete_rrset_by_id(record_set["SetIdentifier"]) - else: - the_zone.delete_rrset_by_name(record_set["Name"]) - - return 200, headers, CHANGE_RRSET_RESPONSE - - elif method == "GET": - querystring = parse_qs(parsed_url.query) - template = Template(LIST_RRSET_REPONSE) - type_filter = querystring.get("type", [None])[0] - name_filter = querystring.get("name", [None])[0] - record_sets = the_zone.get_record_sets(type_filter, name_filter) - return 200, headers, template.render(record_sets=record_sets) + action = '' + if 'tags' in full_url: + action = 'tags' + elif 'trafficpolicyinstances' in full_url: + action = 'policies' + raise NotImplementedError("The action for {0} has not been implemented for route 53".format(action)) -def health_check_response(request, full_url, headers): - parsed_url = urlparse(full_url) - method = request.method + def list_or_change_tags_for_resource_request(self, request, full_url, headers): + self.setup_class(request, full_url, headers) - if method == "POST": - properties = xmltodict.parse(request.body)['CreateHealthCheckRequest']['HealthCheckConfig'] - health_check_args = { - "ip_address": properties.get('IPAddress'), - "port": properties.get('Port'), - "type": properties['Type'], - "resource_path": properties.get('ResourcePath'), - "fqdn": properties.get('FullyQualifiedDomainName'), - "search_string": properties.get('SearchString'), - "request_interval": properties.get('RequestInterval'), - "failure_threshold": properties.get('FailureThreshold'), - } - health_check = route53_backend.create_health_check(health_check_args) - template = Template(CREATE_HEALTH_CHECK_RESPONSE) - return 201, headers, template.render(health_check=health_check) - elif method == "DELETE": - health_check_id = parsed_url.path.split("/")[-1] - route53_backend.delete_health_check(health_check_id) - return 200, headers, DELETE_HEALTH_CHECK_REPONSE - elif method == "GET": - template = Template(LIST_HEALTH_CHECKS_REPONSE) - health_checks = route53_backend.get_health_checks() - return 200, headers, template.render(health_checks=health_checks) + parsed_url = urlparse(full_url) + id_ = parsed_url.path.split("/")[-1] + type_ = parsed_url.path.split("/")[-2] -def not_implemented_response(request, full_url, headers): - action = '' - if 'tags' in full_url: - action = 'tags' - elif 'trafficpolicyinstances' in full_url: - action = 'policies' - raise NotImplementedError("The action for {0} has not been implemented for route 53".format(action)) + if request.method == "GET": + tags = route53_backend.list_tags_for_resource(id_) + template = Template(LIST_TAGS_FOR_RESOURCE_RESPONSE) + return 200, headers, template.render( + resource_type=type_, resource_id=id_, tags=tags) + if request.method == "POST": + tags = xmltodict.parse( + self.body)['ChangeTagsForResourceRequest'] -def list_or_change_tags_for_resource_request(request, full_url, headers): - parsed_url = urlparse(full_url) - id_ = parsed_url.path.split("/")[-1] - type_ = parsed_url.path.split("/")[-2] + if 'AddTags' in tags: + tags = tags['AddTags'] + elif 'RemoveTagKeys' in tags: + tags = tags['RemoveTagKeys'] - if request.method == "GET": - tags = route53_backend.list_tags_for_resource(id_) - template = Template(LIST_TAGS_FOR_RESOURCE_RESPONSE) - return 200, headers, template.render( - resource_type=type_, resource_id=id_, tags=tags) + route53_backend.change_tags_for_resource(id_, tags) + template = Template(CHANGE_TAGS_FOR_RESOURCE_RESPONSE) - if request.method == "POST": - tags = xmltodict.parse( - request.body)['ChangeTagsForResourceRequest'] - - if 'AddTags' in tags: - tags = tags['AddTags'] - elif 'RemoveTagKeys' in tags: - tags = tags['RemoveTagKeys'] - - route53_backend.change_tags_for_resource(id_, tags) - template = Template(CHANGE_TAGS_FOR_RESOURCE_RESPONSE) - - return 200, headers, template.render() + return 200, headers, template.render() LIST_TAGS_FOR_RESOURCE_RESPONSE = """ diff --git a/moto/route53/urls.py b/moto/route53/urls.py index 361c9631..795f7d80 100644 --- a/moto/route53/urls.py +++ b/moto/route53/urls.py @@ -1,15 +1,25 @@ from __future__ import unicode_literals -from . import responses +from .responses import Route53 url_bases = [ - "https://route53.amazonaws.com/201.-..-../", + "https?://route53(.*).amazonaws.com", ] + +def tag_response1(*args, **kwargs): + return Route53().list_or_change_tags_for_resource_request(*args, **kwargs) + + +def tag_response2(*args, **kwargs): + return Route53().list_or_change_tags_for_resource_request(*args, **kwargs) + + url_paths = { - '{0}hostedzone$': responses.list_or_create_hostzone_response, - '{0}hostedzone/[^/]+$': responses.get_or_delete_hostzone_response, - '{0}hostedzone/[^/]+/rrset/?$': responses.rrset_response, - '{0}healthcheck': responses.health_check_response, - '{0}tags/(healthcheck|hostedzone)/*': responses.list_or_change_tags_for_resource_request, - '{0}trafficpolicyinstances/*': responses.not_implemented_response + '{0}/(?P[\d_-]+)/hostedzone$': Route53().list_or_create_hostzone_response, + '{0}/(?P[\d_-]+)/hostedzone/(?P[^/]+)$': Route53().get_or_delete_hostzone_response, + '{0}/(?P[\d_-]+)/hostedzone/(?P[^/]+)/rrset/?$': Route53().rrset_response, + '{0}/(?P[\d_-]+)/healthcheck': Route53().health_check_response, + '{0}/(?P[\d_-]+)/tags/healthcheck/(?P[^/]+)$': tag_response1, + '{0}/(?P[\d_-]+)/tags/hostedzone/(?P[^/]+)$': tag_response2, + '{0}/(?P[\d_-]+)/trafficpolicyinstances/*': Route53().not_implemented_response } diff --git a/moto/s3/__init__.py b/moto/s3/__init__.py index 7d0df53b..2c54a8d5 100644 --- a/moto/s3/__init__.py +++ b/moto/s3/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import s3_backend + +s3_backends = {"global": s3_backend} mock_s3 = s3_backend.decorator mock_s3_deprecated = s3_backend.deprecated_decorator \ No newline at end of file diff --git a/moto/s3/models.py b/moto/s3/models.py index 40370b5d..d5e15649 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -89,21 +89,21 @@ class FakeKey(object): @property def response_dict(self): - r = { + res = { 'etag': self.etag, 'last-modified': self.last_modified_RFC1123, 'content-length': str(len(self.value)), } if self._storage_class != 'STANDARD': - r['x-amz-storage-class'] = self._storage_class + res['x-amz-storage-class'] = self._storage_class if self._expiry is not None: rhdr = 'ongoing-request="false", expiry-date="{0}"' - r['x-amz-restore'] = rhdr.format(self.expiry_date) + res['x-amz-restore'] = rhdr.format(self.expiry_date) if self._is_versioned: - r['x-amz-version-id'] = str(self._version_id) + res['x-amz-version-id'] = str(self._version_id) - return r + return res @property def size(self): diff --git a/moto/server.py b/moto/server.py index 0b5ff7ca..0bb4eb77 100644 --- a/moto/server.py +++ b/moto/server.py @@ -39,21 +39,28 @@ class DomainDispatcherApplication(object): return host for backend_name, backend in BACKENDS.items(): - for url_base in backend.url_bases: + for url_base in backend.values()[0].url_bases: if re.match(url_base, 'http://%s' % host): return backend_name raise RuntimeError('Invalid host: "%s"' % host) def get_application(self, environ): - if environ.get('PATH_INFO', '').startswith("/moto-api"): + path_info = environ.get('PATH_INFO', '') + if path_info.startswith("/moto-api"): host = "moto_api" + elif path_info.startswith("/latest/meta-data/"): + host = "instance_metadata" else: host = environ['HTTP_HOST'].split(':')[0] if host == "localhost": # Fall back to parsing auth header to find service # ['Credential=sdffdsa', '20170220', 'us-east-1', 'sns', 'aws4_request'] - _, _, region, service, _ = environ['HTTP_AUTHORIZATION'].split(",")[0].split()[1].split("/") + try: + _, _, region, service, _ = environ['HTTP_AUTHORIZATION'].split(",")[0].split()[1].split("/") + except ValueError: + region = 'us-east-1' + service = 's3' host = "{service}.{region}.amazonaws.com".format(service=service, region=region) with self.lock: @@ -108,7 +115,7 @@ def create_backend_app(service): backend_app.view_functions = {} backend_app.url_map = Map() backend_app.url_map.converters['regex'] = RegexConverter - backend = BACKENDS[service] + backend = BACKENDS[service].values()[0] for url_path, handler in backend.flask_paths.items(): if handler.__name__ == 'dispatch': endpoint = '{0}.dispatch'.format(handler.__self__.__name__) diff --git a/moto/ses/__init__.py b/moto/ses/__init__.py index e1ec4b41..e105b992 100644 --- a/moto/ses/__init__.py +++ b/moto/ses/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import ses_backend + +ses_backends = {"global": ses_backend} mock_ses = ses_backend.decorator mock_ses_deprecated = ses_backend.deprecated_decorator \ No newline at end of file diff --git a/moto/ses/urls.py b/moto/ses/urls.py index 18d5874c..adfb4c6e 100644 --- a/moto/ses/urls.py +++ b/moto/ses/urls.py @@ -2,7 +2,8 @@ from __future__ import unicode_literals from .responses import EmailResponse url_bases = [ - "https?://email.(.+).amazonaws.com" + "https?://email.(.+).amazonaws.com", + "https?://ses.(.+).amazonaws.com", ] url_paths = { diff --git a/moto/settings.py b/moto/settings.py new file mode 100644 index 00000000..a5240f13 --- /dev/null +++ b/moto/settings.py @@ -0,0 +1,3 @@ +import os + +TEST_SERVER_MODE = os.environ.get('TEST_SERVER_MODE', '0').lower() == 'true' diff --git a/moto/sts/__init__.py b/moto/sts/__init__.py index 57456c1b..7b46bdfb 100644 --- a/moto/sts/__init__.py +++ b/moto/sts/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals from .models import sts_backend + +sts_backends = {"global": sts_backend} mock_sts = sts_backend.decorator mock_sts_deprecated = sts_backend.deprecated_decorator diff --git a/moto/sts/urls.py b/moto/sts/urls.py index c6e31096..2078e0b2 100644 --- a/moto/sts/urls.py +++ b/moto/sts/urls.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from .responses import TokenResponse url_bases = [ - "https?://sts.amazonaws.com" + "https?://sts(.*).amazonaws.com" ] url_paths = { diff --git a/other_langs/sqsSample.java b/other_langs/sqsSample.java new file mode 100644 index 00000000..23368272 --- /dev/null +++ b/other_langs/sqsSample.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.amazonaws.samples; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClient; + +public class S3Sample { + + public static void main(String[] args) throws IOException { + AmazonSQS sqs = new AmazonSQSClient(); + Region usWest2 = Region.getRegion(Regions.US_WEST_2); + sqs.setRegion(usWest2); + sqs.setEndpoint("http://localhost:8086"); + + String queueName = "my-first-queue"; + sqs.createQueue(queueName); + + System.out.println("Listing queues"); + for (String queue_url: sqs.listQueues().getQueueUrls()) { + System.out.println(" - " + queue_url); + } + System.out.println(); + + } + +} diff --git a/other_langs/test.js b/other_langs/test.js new file mode 100644 index 00000000..65d65ae7 --- /dev/null +++ b/other_langs/test.js @@ -0,0 +1,26 @@ +var AWS = require('aws-sdk'); + +var s3 = new AWS.S3({endpoint: "http://localhost:8086"}); +var myBucket = 'my.unique.bucket.name'; + +var myKey = 'myBucketKey'; + +s3.createBucket({Bucket: myBucket}, function(err, data) { + if (err) { + console.log(err); + } else { + params = {Bucket: myBucket, Key: myKey, Body: 'Hello!'}; + s3.putObject(params, function(err, data) { + if (err) { + console.log(err) + } else { + console.log("Successfully uploaded data to myBucket/myKey"); + } + }); + } +}); + +s3.listBuckets(function(err, data) { + if (err) console.log(err, err.stack); // an error occurred + else console.log(data); // successful response +}); diff --git a/other_langs/test.rb b/other_langs/test.rb new file mode 100644 index 00000000..dc5b7914 --- /dev/null +++ b/other_langs/test.rb @@ -0,0 +1,6 @@ +require 'aws-sdk' + +sqs = Aws::SQS::Resource.new(region: 'us-west-2', endpoint: 'http://localhost:8086') +my_queue = sqs.create_queue(queue_name: 'my-bucket') + +puts sqs.client.list_queues() diff --git a/setup.cfg b/setup.cfg index 3480374b..3c6e79cf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ [bdist_wheel] -universal=1 \ No newline at end of file +universal=1 diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py index 6bd6eb5e..e52bfe0d 100644 --- a/tests/test_apigateway/test_apigateway.py +++ b/tests/test_apigateway/test_apigateway.py @@ -10,7 +10,7 @@ import sure # noqa from botocore.exceptions import ClientError from moto.packages.responses import responses -from moto import mock_apigateway +from moto import mock_apigateway, settings @freeze_time("2015-01-01") @@ -29,11 +29,11 @@ def test_create_and_get_rest_api(): ) response.pop('ResponseMetadata') + response.pop('createdDate') response.should.equal({ 'id': api_id, 'name': 'my_api', 'description': 'this is my api', - 'createdDate': datetime(2015, 1, 1, tzinfo=tzutc()) }) @@ -930,4 +930,5 @@ def test_http_proxying_integration(): deploy_url = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}".format(api_id=api_id, region_name=region_name, stage_name=stage_name) - requests.get(deploy_url).content.should.equal(b"a fake response") + if not settings.TEST_SERVER_MODE: + requests.get(deploy_url).content.should.equal(b"a fake response") diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py index ce8892dc..74e93c37 100644 --- a/tests/test_awslambda/test_lambda.py +++ b/tests/test_awslambda/test_lambda.py @@ -10,7 +10,7 @@ import zipfile import sure # noqa from freezegun import freeze_time -from moto import mock_lambda, mock_s3, mock_ec2 +from moto import mock_lambda, mock_s3, mock_ec2, settings def _process_lamda(pfunc): @@ -36,16 +36,15 @@ def lambda_handler(event, context): volume_id = event.get('volume_id') print('get volume details for %s' % volume_id) import boto3 - ec2 = boto3.resource('ec2', region_name='us-west-2') + ec2 = boto3.resource('ec2', region_name='us-west-2', endpoint_url="http://{base_url}") vol = ec2.Volume(volume_id) print('Volume - %s state=%s, size=%s' % (volume_id, vol.state, vol.size)) return event -""" +""".format(base_url="localhost:8086" if settings.TEST_SERVER_MODE else "ec2.us-west-2.amazonaws.com") return _process_lamda(pfunc) @mock_lambda -@mock_s3 def test_list_functions(): conn = boto3.client('lambda', 'us-west-2') result = conn.list_functions() @@ -53,7 +52,6 @@ def test_list_functions(): @mock_lambda -@freeze_time('2015-01-01 00:00:00') def test_invoke_requestresponse_function(): conn = boto3.client('lambda', 'us-west-2') conn.create_function( @@ -80,7 +78,6 @@ def test_invoke_requestresponse_function(): @mock_lambda -@freeze_time('2015-01-01 00:00:00') def test_invoke_event_function(): conn = boto3.client('lambda', 'us-west-2') conn.create_function( @@ -111,7 +108,6 @@ def test_invoke_event_function(): @mock_ec2 @mock_lambda -@freeze_time('2015-01-01 00:00:00') def test_invoke_function_get_ec2_volume(): conn = boto3.resource("ec2", "us-west-2") vol = conn.create_volume(Size=99, AvailabilityZone='us-west-2') @@ -141,7 +137,6 @@ def test_invoke_function_get_ec2_volume(): @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') @@ -196,6 +191,7 @@ def test_create_function_from_aws_bucket(): ) result['ResponseMetadata'].pop('HTTPHeaders', None) # this is hard to match against, so remove it result['ResponseMetadata'].pop('RetryAttempts', None) # Botocore inserts retry attempts not seen in Python27 + result.pop('LastModified') result.should.equal({ 'FunctionName': 'testFunction', 'FunctionArn': 'arn:aws:lambda:123456789012:function:testFunction', @@ -207,7 +203,6 @@ def test_create_function_from_aws_bucket(): 'Description': 'test lambda function', 'Timeout': 3, 'MemorySize': 128, - 'LastModified': '2015-01-01 00:00:00', 'Version': '$LATEST', 'VpcConfig': { "SecurityGroupIds": ["sg-123abc"], @@ -238,6 +233,7 @@ def test_create_function_from_zipfile(): ) result['ResponseMetadata'].pop('HTTPHeaders', None) # this is hard to match against, so remove it result['ResponseMetadata'].pop('RetryAttempts', None) # Botocore inserts retry attempts not seen in Python27 + result.pop('LastModified') result.should.equal({ 'FunctionName': 'testFunction', @@ -249,7 +245,6 @@ def test_create_function_from_zipfile(): 'Description': 'test lambda function', 'Timeout': 3, 'MemorySize': 128, - 'LastModified': '2015-01-01 00:00:00', 'CodeSha256': hashlib.sha256(zip_content).hexdigest(), 'Version': '$LATEST', 'VpcConfig': { @@ -290,6 +285,7 @@ def test_get_function(): result = conn.get_function(FunctionName='testFunction') result['ResponseMetadata'].pop('HTTPHeaders', None) # this is hard to match against, so remove it result['ResponseMetadata'].pop('RetryAttempts', None) # Botocore inserts retry attempts not seen in Python27 + result['Configuration'].pop('LastModified') result.should.equal({ "Code": { @@ -303,7 +299,6 @@ def test_get_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", @@ -395,7 +390,6 @@ def test_list_create_list_get_delete_list(): "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", @@ -408,11 +402,14 @@ def test_list_create_list_get_delete_list(): }, 'ResponseMetadata': {'HTTPStatusCode': 200}, } - conn.list_functions()['Functions'].should.equal([expected_function_result['Configuration']]) + func = conn.list_functions()['Functions'][0] + func.pop('LastModified') + func.should.equal(expected_function_result['Configuration']) func = conn.get_function(FunctionName='testFunction') func['ResponseMetadata'].pop('HTTPHeaders', None) # this is hard to match against, so remove it func['ResponseMetadata'].pop('RetryAttempts', None) # Botocore inserts retry attempts not seen in Python27 + func['Configuration'].pop('LastModified') func.should.equal(expected_function_result) conn.delete_function(FunctionName='testFunction') diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py index 95ac6ede..2ee74f88 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py @@ -5,7 +5,7 @@ import boto import boto.s3 import boto.s3.key from botocore.exceptions import ClientError -from moto import mock_cloudformation, mock_s3_deprecated +from moto import mock_cloudformation, mock_s3 import json import sure # noqa @@ -118,14 +118,20 @@ def test_create_stack_with_role_arn(): @mock_cloudformation -@mock_s3_deprecated +@mock_s3 def test_create_stack_from_s3_url(): - s3_conn = boto.s3.connect_to_region('us-west-1') - bucket = s3_conn.create_bucket("foobar") - key = boto.s3.key.Key(bucket) - key.key = "template-key" - key.set_contents_from_string(dummy_template_json) - key_url = key.generate_url(expires_in=0, query_auth=False) + s3 = boto3.client('s3') + s3_conn = boto3.resource('s3') + bucket = s3_conn.create_bucket(Bucket="foobar") + + key = s3_conn.Object('foobar', 'template-key').put(Body=dummy_template_json) + key_url = s3.generate_presigned_url( + ClientMethod='get_object', + Params={ + 'Bucket': 'foobar', + 'Key': 'template-key' + } + ) cf_conn = boto3.client('cloudformation', region_name='us-west-1') cf_conn.create_stack( diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 1b9330a9..609a0b46 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -701,27 +701,29 @@ def test_vpc_single_instance_in_subnet(): eip_resource = [resource for resource in resources if resource.resource_type == 'AWS::EC2::EIP'][0] eip_resource.physical_resource_id.should.equal(eip.allocation_id) -@mock_cloudformation_deprecated() -@mock_ec2_deprecated() +@mock_cloudformation() +@mock_ec2() @mock_rds2() def test_rds_db_parameter_groups(): - ec2_conn = boto.ec2.connect_to_region("us-west-1") - ec2_conn.create_security_group('application', 'Our Application Group') + ec2_conn = boto3.client("ec2", region_name="us-west-1") + ec2_conn.create_security_group(GroupName='application', Description='Our Application Group') template_json = json.dumps(rds_mysql_with_db_parameter_group.template) - conn = boto.cloudformation.connect_to_region("us-west-1") - conn.create_stack( - "test_stack", - template_body=template_json, - parameters=[ - ("DBInstanceIdentifier", "master_db"), - ("DBName", "my_db"), - ("DBUser", "my_user"), - ("DBPassword", "my_password"), - ("DBAllocatedStorage", "20"), - ("DBInstanceClass", "db.m1.medium"), - ("EC2SecurityGroup", "application"), - ("MultiAZ", "true"), + cf_conn = boto3.client('cloudformation', 'us-west-1') + cf_conn.create_stack( + StackName="test_stack", + TemplateBody=template_json, + Parameters=[{'ParameterKey': key, 'ParameterValue': value} for + key, value in [ + ("DBInstanceIdentifier", "master_db"), + ("DBName", "my_db"), + ("DBUser", "my_user"), + ("DBPassword", "my_password"), + ("DBAllocatedStorage", "20"), + ("DBInstanceClass", "db.m1.medium"), + ("EC2SecurityGroup", "application"), + ("MultiAZ", "true"), + ] ], ) @@ -1802,7 +1804,7 @@ def lambda_handler(event, context): return _process_lamda(pfunc) -@mock_cloudformation_deprecated +@mock_cloudformation @mock_lambda def test_lambda_function(): # switch this to python as backend lambda only supports python execution. @@ -1826,10 +1828,10 @@ def test_lambda_function(): } template_json = json.dumps(template) - cf_conn = boto.cloudformation.connect_to_region("us-east-1") + cf_conn = boto3.client('cloudformation', 'us-east-1') cf_conn.create_stack( - "test_stack", - template_body=template_json, + StackName="test_stack", + TemplateBody=template_json, ) conn = boto3.client('lambda', 'us-east-1') diff --git a/tests/test_core/test_instance_metadata.py b/tests/test_core/test_instance_metadata.py index aa86b41b..80dd501e 100644 --- a/tests/test_core/test_instance_metadata.py +++ b/tests/test_core/test_instance_metadata.py @@ -3,18 +3,23 @@ import sure # noqa from nose.tools import assert_raises import requests -from moto import mock_ec2 +from moto import mock_ec2, settings + +if settings.TEST_SERVER_MODE: + BASE_URL = 'http://localhost:8086' +else: + BASE_URL = 'http://169.254.169.254' @mock_ec2 def test_latest_meta_data(): - res = requests.get("http://169.254.169.254/latest/meta-data/") + res = requests.get("{0}/latest/meta-data/".format(BASE_URL)) res.content.should.equal(b"iam") @mock_ec2 def test_meta_data_iam(): - res = requests.get("http://169.254.169.254/latest/meta-data/iam") + res = requests.get("{0}/latest/meta-data/iam".format(BASE_URL)) json_response = res.json() default_role = json_response['security-credentials']['default-role'] default_role.should.contain('AccessKeyId') @@ -25,21 +30,15 @@ def test_meta_data_iam(): @mock_ec2 def test_meta_data_security_credentials(): - res = requests.get("http://169.254.169.254/latest/meta-data/iam/security-credentials/") + res = requests.get("{0}/latest/meta-data/iam/security-credentials/".format(BASE_URL)) res.content.should.equal(b"default-role") @mock_ec2 def test_meta_data_default_role(): - res = requests.get("http://169.254.169.254/latest/meta-data/iam/security-credentials/default-role") + res = requests.get("{0}/latest/meta-data/iam/security-credentials/default-role".format(BASE_URL)) json_response = res.json() json_response.should.contain('AccessKeyId') json_response.should.contain('SecretAccessKey') json_response.should.contain('Token') json_response.should.contain('Expiration') - - -@mock_ec2 -def test_meta_data_unknown_path(): - with assert_raises(NotImplementedError): - requests.get("http://169.254.169.254/latest/meta-data/badpath") diff --git a/tests/test_core/test_moto_api.py b/tests/test_core/test_moto_api.py new file mode 100644 index 00000000..3b441a3f --- /dev/null +++ b/tests/test_core/test_moto_api.py @@ -0,0 +1,21 @@ +from __future__ import unicode_literals +import sure # noqa +from nose.tools import assert_raises +import requests + +import boto3 +from moto import mock_sqs, settings + +base_url = "http://localhost:8086" if settings.TEST_SERVER_MODE else "http://motoapi.amazonaws.com" + + +@mock_sqs +def test_reset_api(): + conn = boto3.client("sqs", region_name='us-west-1') + conn.create_queue(QueueName="queue1") + conn.list_queues()['QueueUrls'].should.have.length_of(1) + + res = requests.post("{base_url}/moto-api/reset".format(base_url=base_url)) + res.content.should.equal(b'{"status": "ok"}') + + conn.list_queues().shouldnt.contain('QueueUrls') # No more queues diff --git a/tests/test_dynamodb/test_dynamodb.py b/tests/test_dynamodb/test_dynamodb.py index 7ea56faa..f2df39a2 100644 --- a/tests/test_dynamodb/test_dynamodb.py +++ b/tests/test_dynamodb/test_dynamodb.py @@ -42,13 +42,6 @@ def test_describe_missing_table(): conn.describe_table('messages') -@mock_dynamodb -def test_sts_handler(): - res = requests.post("https://sts.amazonaws.com/", data={"GetSessionToken": ""}) - res.ok.should.be.ok - res.text.should.contain("SecretAccessKey") - - @mock_dynamodb_deprecated def test_dynamodb_with_connect_to_region(): # this will work if connected with boto.connect_dynamodb() diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index d66d36d9..9e92e798 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -63,11 +63,3 @@ def test_describe_missing_table(): aws_secret_access_key="sk") with assert_raises(JSONResponseError): conn.describe_table('messages') - - -@requires_boto_gte("2.9") -@mock_dynamodb2 -def test_sts_handler(): - res = requests.post("https://sts.amazonaws.com/", data={"GetSessionToken": ""}) - res.ok.should.be.ok - res.text.should.contain("SecretAccessKey") diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py index 9c3fbd40..4c154ae8 100755 --- a/tests/test_ec2/test_amis.py +++ b/tests/test_ec2/test_amis.py @@ -5,7 +5,7 @@ from nose.tools import assert_raises import boto import boto.ec2 -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError, EC2ResponseError import sure # noqa @@ -19,9 +19,9 @@ def test_ami_create_and_delete(): reservation = conn.run_instances('ami-1234abcd') instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: image_id = conn.create_image(instance.id, "test-ami", "this is a test ami", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateImage operation: Request would have succeeded, but DryRun flag is set') @@ -52,9 +52,9 @@ def test_ami_create_and_delete(): snapshot.volume_id.should.equal(volume.id) # Deregister - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: success = conn.deregister_image(image_id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeregisterImage operation: Request would have succeeded, but DryRun flag is set') @@ -80,9 +80,9 @@ def test_ami_copy(): source_image = conn.get_all_images(image_ids=[source_image_id])[0] # Boto returns a 'CopyImage' object with an image_id attribute here. Use the image_id to fetch the full info. - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: copy_image_ref = conn.copy_image(source_image.region.name, source_image.id, "test-copy-ami", "this is a test copy ami", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CopyImage operation: Request would have succeeded, but DryRun flag is set') @@ -127,9 +127,9 @@ def test_ami_tagging(): conn.create_image(instance.id, "test-ami", "this is a test ami") image = conn.get_all_images()[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: image.add_tag("a key", "some value", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') @@ -289,9 +289,9 @@ def test_ami_attribute_group_permissions(): 'groups': 'all'} # Add 'all' group and confirm - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.modify_image_attribute(**dict(ADD_GROUP_ARGS, **{'dry_run': True})) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyImageAttribute operation: Request would have succeeded, but DryRun flag is set') diff --git a/tests/test_ec2/test_ec2_core.py b/tests/test_ec2/test_ec2_core.py index 53c7d648..baffc488 100644 --- a/tests/test_ec2/test_ec2_core.py +++ b/tests/test_ec2/test_ec2_core.py @@ -1,11 +1 @@ from __future__ import unicode_literals -import requests -from moto import mock_ec2 - - -@mock_ec2 -def test_not_implemented_method(): - requests.post.when.called_with( - "https://ec2.us-east-1.amazonaws.com/", - data={'Action': ['foobar']} - ).should.throw(NotImplementedError) diff --git a/tests/test_ec2/test_elastic_block_store.py b/tests/test_ec2/test_elastic_block_store.py index c4794b1c..6491412e 100644 --- a/tests/test_ec2/test_elastic_block_store.py +++ b/tests/test_ec2/test_elastic_block_store.py @@ -5,7 +5,7 @@ from nose.tools import assert_raises from moto.ec2 import ec2_backends import boto -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError import sure # noqa from moto import mock_ec2_deprecated @@ -24,9 +24,9 @@ def test_create_and_delete_volume(): volume = all_volumes[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: volume.delete(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteVolume operation: Request would have succeeded, but DryRun flag is set') @@ -46,9 +46,9 @@ def test_create_and_delete_volume(): @mock_ec2_deprecated def test_create_encrypted_volume_dryrun(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.create_volume(80, "us-east-1a", encrypted=True, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateVolume operation: Request would have succeeded, but DryRun flag is set') @@ -58,9 +58,9 @@ def test_create_encrypted_volume(): conn = boto.connect_ec2('the_key', 'the_secret') conn.create_volume(80, "us-east-1a", encrypted=True) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.create_volume(80, "us-east-1a", encrypted=True, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateVolume operation: Request would have succeeded, but DryRun flag is set') @@ -165,9 +165,9 @@ def test_volume_attach_and_detach(): volume.update() volume.volume_state().should.equal('available') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: volume.attach(instance.id, "/dev/sdh", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AttachVolume operation: Request would have succeeded, but DryRun flag is set') @@ -179,9 +179,9 @@ def test_volume_attach_and_detach(): volume.attach_data.instance_id.should.equal(instance.id) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: volume.detach(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DetachVolume operation: Request would have succeeded, but DryRun flag is set') @@ -214,9 +214,9 @@ def test_create_snapshot(): conn = boto.connect_ec2('the_key', 'the_secret') volume = conn.create_volume(80, "us-east-1a") - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: snapshot = volume.create_snapshot('a dryrun snapshot', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateSnapshot operation: Request would have succeeded, but DryRun flag is set') @@ -347,9 +347,9 @@ def test_snapshot_attribute(): # Add 'all' group and confirm - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.modify_snapshot_attribute(**dict(ADD_GROUP_ARGS, **{'dry_run': True})) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifySnapshotAttribute operation: Request would have succeeded, but DryRun flag is set') @@ -363,9 +363,9 @@ def test_snapshot_attribute(): conn.modify_snapshot_attribute.when.called_with(**ADD_GROUP_ARGS).should_not.throw(EC2ResponseError) # Remove 'all' group and confirm - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.modify_snapshot_attribute(**dict(REMOVE_GROUP_ARGS, **{'dry_run': True})) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifySnapshotAttribute operation: Request would have succeeded, but DryRun flag is set') @@ -424,9 +424,9 @@ def test_create_volume_from_snapshot(): volume = conn.create_volume(80, "us-east-1a") snapshot = volume.create_snapshot('a test snapshot') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: snapshot = volume.create_snapshot('a test snapshot', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateSnapshot operation: Request would have succeeded, but DryRun flag is set') @@ -468,9 +468,9 @@ def test_modify_attribute_blockDeviceMapping(): instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.modify_attribute('blockDeviceMapping', {'/dev/sda1': True}, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyInstanceAttribute operation: Request would have succeeded, but DryRun flag is set') @@ -487,9 +487,9 @@ def test_volume_tag_escaping(): vol = conn.create_volume(10, 'us-east-1a') snapshot = conn.create_snapshot(vol.id, 'Desc') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: snapshot.add_tags({'key': ''}, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') dict(conn.get_all_snapshots()[0].tags).should_not.be.equal({'key': ''}) diff --git a/tests/test_ec2/test_elastic_ip_addresses.py b/tests/test_ec2/test_elastic_ip_addresses.py index dc791037..f92c4df8 100644 --- a/tests/test_ec2/test_elastic_ip_addresses.py +++ b/tests/test_ec2/test_elastic_ip_addresses.py @@ -5,7 +5,7 @@ from nose.tools import assert_raises import boto import boto3 -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError import six import sure # noqa @@ -20,9 +20,9 @@ def test_eip_allocate_classic(): """Allocate/release Classic EIP""" conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: standard = conn.allocate_address(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AllocateAddress operation: Request would have succeeded, but DryRun flag is set') @@ -32,9 +32,9 @@ def test_eip_allocate_classic(): standard.instance_id.should.be.none standard.domain.should.be.equal("standard") - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: standard.release(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ReleaseAddress operation: Request would have succeeded, but DryRun flag is set') @@ -47,9 +47,9 @@ def test_eip_allocate_vpc(): """Allocate/release VPC EIP""" conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: vpc = conn.allocate_address(domain="vpc", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AllocateAddress operation: Request would have succeeded, but DryRun flag is set') @@ -89,9 +89,9 @@ def test_eip_associate_classic(): cm.exception.status.should.equal(400) cm.exception.request_id.should_not.be.none - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.associate_address(instance_id=instance.id, public_ip=eip.public_ip, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AssociateAddress operation: Request would have succeeded, but DryRun flag is set') @@ -99,9 +99,9 @@ def test_eip_associate_classic(): eip = conn.get_all_addresses(addresses=[eip.public_ip])[0] # no .update() on address ): eip.instance_id.should.be.equal(instance.id) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.disassociate_address(public_ip=eip.public_ip, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DisAssociateAddress operation: Request would have succeeded, but DryRun flag is set') @@ -139,9 +139,9 @@ def test_eip_associate_vpc(): eip.instance_id.should.be.equal(u'') eip.association_id.should.be.none - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: eip.release(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ReleaseAddress operation: Request would have succeeded, but DryRun flag is set') @@ -153,9 +153,8 @@ def test_eip_associate_vpc(): @mock_ec2 def test_eip_boto3_vpc_association(): """Associate EIP to VPC instance in a new subnet with boto3""" - session = boto3.session.Session(region_name='us-west-1') - service = session.resource('ec2') - client = session.client('ec2') + service = boto3.resource('ec2', region_name='us-west-1') + client = boto3.client('ec2', region_name='us-west-1') vpc_res = client.create_vpc(CidrBlock='10.0.0.0/24') subnet_res = client.create_subnet( VpcId=vpc_res['Vpc']['VpcId'], CidrBlock='10.0.0.0/24') diff --git a/tests/test_ec2/test_elastic_network_interfaces.py b/tests/test_ec2/test_elastic_network_interfaces.py index 6f60c85a..9027e044 100644 --- a/tests/test_ec2/test_elastic_network_interfaces.py +++ b/tests/test_ec2/test_elastic_network_interfaces.py @@ -4,10 +4,11 @@ import tests.backport_assert_raises from nose.tools import assert_raises import boto3 +from botocore.exceptions import ClientError import boto import boto.cloudformation import boto.ec2 -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError import sure # noqa from moto import mock_ec2, mock_cloudformation_deprecated, mock_ec2_deprecated @@ -22,9 +23,9 @@ def test_elastic_network_interfaces(): vpc = conn.create_vpc("10.0.0.0/16") subnet = conn.create_subnet(vpc.id, "10.0.0.0/18") - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: eni = conn.create_network_interface(subnet.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateNetworkInterface operation: Request would have succeeded, but DryRun flag is set') @@ -36,9 +37,9 @@ def test_elastic_network_interfaces(): eni.groups.should.have.length_of(0) eni.private_ip_addresses.should.have.length_of(0) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.delete_network_interface(eni.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteNetworkInterface operation: Request would have succeeded, but DryRun flag is set') @@ -49,7 +50,7 @@ def test_elastic_network_interfaces(): with assert_raises(EC2ResponseError) as cm: conn.delete_network_interface(eni.id) - cm.exception.code.should.equal('InvalidNetworkInterfaceID.NotFound') + cm.exception.error_code.should.equal('InvalidNetworkInterfaceID.NotFound') cm.exception.status.should.equal(400) cm.exception.request_id.should_not.be.none @@ -60,7 +61,7 @@ def test_elastic_network_interfaces_subnet_validation(): with assert_raises(EC2ResponseError) as cm: conn.create_network_interface("subnet-abcd1234") - cm.exception.code.should.equal('InvalidSubnetID.NotFound') + cm.exception.error_code.should.equal('InvalidSubnetID.NotFound') cm.exception.status.should.equal(400) cm.exception.request_id.should_not.be.none @@ -117,9 +118,9 @@ def test_elastic_network_interfaces_modify_attribute(): eni.groups.should.have.length_of(1) eni.groups[0].id.should.equal(security_group1.id) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.modify_network_interface_attribute(eni.id, 'groupset', [security_group2.id], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyNetworkInterface operation: Request would have succeeded, but DryRun flag is set') @@ -183,11 +184,11 @@ def test_elastic_network_interfaces_get_by_tag_name(): eni1 = ec2.create_network_interface(SubnetId=subnet.id, PrivateIpAddress='10.0.10.5') - with assert_raises(JSONResponseError) as ex: + with assert_raises(ClientError) as ex: eni1.create_tags(Tags=[{'Key': 'Name', 'Value': 'eni1'}], DryRun=True) - ex.exception.reason.should.equal('DryRunOperation') - ex.exception.status.should.equal(400) - ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') + ex.exception.response['Error']['Code'].should.equal('DryRunOperation') + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') eni1.create_tags(Tags=[{'Key': 'Name', 'Value': 'eni1'}]) diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py index a310c05a..b6601e87 100644 --- a/tests/test_ec2/test_instances.py +++ b/tests/test_ec2/test_instances.py @@ -8,7 +8,7 @@ import datetime import boto from boto.ec2.instance import Reservation, InstanceAttribute -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError, EC2ResponseError from freezegun import freeze_time import sure # noqa @@ -41,9 +41,9 @@ def test_add_servers(): def test_instance_launch_and_terminate(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: reservation = conn.run_instances('ami-1234abcd', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the RunInstance operation: Request would have succeeded, but DryRun flag is set') @@ -74,9 +74,9 @@ def test_instance_launch_and_terminate(): volume.attach_data.instance_id.should.equal(instance.id) volume.status.should.equal('in-use') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.terminate_instances([instance.id], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the TerminateInstance operation: Request would have succeeded, but DryRun flag is set') @@ -427,9 +427,9 @@ def test_instance_start_and_stop(): instance_ids = [instance.id for instance in instances] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: stopped_instances = conn.stop_instances(instance_ids, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the StopInstance operation: Request would have succeeded, but DryRun flag is set') @@ -438,9 +438,9 @@ def test_instance_start_and_stop(): for instance in stopped_instances: instance.state.should.equal('stopping') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: started_instances = conn.start_instances([instances[0].id], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the StartInstance operation: Request would have succeeded, but DryRun flag is set') @@ -454,9 +454,9 @@ def test_instance_reboot(): reservation = conn.run_instances('ami-1234abcd') instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.reboot(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the RebootInstance operation: Request would have succeeded, but DryRun flag is set') @@ -470,9 +470,9 @@ def test_instance_attribute_instance_type(): reservation = conn.run_instances('ami-1234abcd') instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.modify_attribute("instanceType", "m1.small", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyInstanceType operation: Request would have succeeded, but DryRun flag is set') @@ -491,9 +491,9 @@ def test_modify_instance_attribute_security_groups(): sg_id = 'sg-1234abcd' sg_id2 = 'sg-abcd4321' - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.modify_attribute("groupSet", [sg_id, sg_id2], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyInstanceSecurityGroups operation: Request would have succeeded, but DryRun flag is set') @@ -512,9 +512,9 @@ def test_instance_attribute_user_data(): reservation = conn.run_instances('ami-1234abcd') instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.modify_attribute("userData", "this is my user data", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyUserData operation: Request would have succeeded, but DryRun flag is set') @@ -540,9 +540,9 @@ def test_instance_attribute_source_dest_check(): # Set to false (note: Boto converts bool to string, eg 'false') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.modify_attribute("sourceDestCheck", False, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifySourceDestCheck operation: Request would have succeeded, but DryRun flag is set') @@ -584,9 +584,9 @@ def test_user_data_with_run_instance(): def test_run_instance_with_security_group_name(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: group = conn.create_security_group('group1', "some description", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateSecurityGroup operation: Request would have succeeded, but DryRun flag is set') @@ -745,9 +745,9 @@ def test_instance_with_nic_attach_detach(): set([group.id for group in eni.groups]).should.equal(set([security_group2.id])) # Attach - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.attach_network_interface(eni.id, instance.id, device_index=1, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AttachNetworkInterface operation: Request would have succeeded, but DryRun flag is set') @@ -766,9 +766,9 @@ def test_instance_with_nic_attach_detach(): set([group.id for group in eni.groups]).should.equal(set([security_group1.id,security_group2.id])) # Detach - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.detach_network_interface(instance_eni.attachment.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DetachNetworkInterface operation: Request would have succeeded, but DryRun flag is set') @@ -886,9 +886,9 @@ def test_get_instance_by_security_group(): security_group = conn.create_security_group('test', 'test') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.modify_instance_attribute(instance.id, "groupSet", [security_group.id], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ModifyInstanceSecurityGroups operation: Request would have succeeded, but DryRun flag is set') diff --git a/tests/test_ec2/test_internet_gateways.py b/tests/test_ec2/test_internet_gateways.py index 12b37860..fe5e4945 100644 --- a/tests/test_ec2/test_internet_gateways.py +++ b/tests/test_ec2/test_internet_gateways.py @@ -6,7 +6,7 @@ from nose.tools import assert_raises import re import boto -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError import sure # noqa @@ -24,9 +24,9 @@ def test_igw_create(): conn.get_all_internet_gateways().should.have.length_of(0) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: igw = conn.create_internet_gateway(dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateInternetGateway operation: Request would have succeeded, but DryRun flag is set') @@ -44,9 +44,9 @@ def test_igw_attach(): igw = conn.create_internet_gateway() vpc = conn.create_vpc(VPC_CIDR) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.attach_internet_gateway(igw.id, vpc.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the AttachInternetGateway operation: Request would have succeeded, but DryRun flag is set') @@ -90,9 +90,9 @@ def test_igw_detach(): vpc = conn.create_vpc(VPC_CIDR) conn.attach_internet_gateway(igw.id, vpc.id) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.detach_internet_gateway(igw.id, vpc.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DetachInternetGateway operation: Request would have succeeded, but DryRun flag is set') @@ -151,9 +151,9 @@ def test_igw_delete(): igw = conn.create_internet_gateway() conn.get_all_internet_gateways().should.have.length_of(1) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.delete_internet_gateway(igw.id, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteInternetGateway operation: Request would have succeeded, but DryRun flag is set') diff --git a/tests/test_ec2/test_key_pairs.py b/tests/test_ec2/test_key_pairs.py index a35f0b96..6c477320 100644 --- a/tests/test_ec2/test_key_pairs.py +++ b/tests/test_ec2/test_key_pairs.py @@ -7,7 +7,7 @@ import boto import six import sure # noqa -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError from moto import mock_ec2_deprecated @@ -32,9 +32,9 @@ def test_key_pairs_invalid_id(): def test_key_pairs_create(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: kp = conn.create_key_pair('foo', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateKeyPair operation: Request would have succeeded, but DryRun flag is set') @@ -87,9 +87,9 @@ def test_key_pairs_delete_exist(): conn = boto.connect_ec2('the_key', 'the_secret') conn.create_key_pair('foo') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: r = conn.delete_key_pair('foo', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteKeyPair operation: Request would have succeeded, but DryRun flag is set') @@ -102,9 +102,9 @@ def test_key_pairs_delete_exist(): def test_key_pairs_import(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: kp = conn.import_key_pair('foo', b'content', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the ImportKeyPair operation: Request would have succeeded, but DryRun flag is set') diff --git a/tests/test_ec2/test_security_groups.py b/tests/test_ec2/test_security_groups.py index 3968d915..3056331b 100644 --- a/tests/test_ec2/test_security_groups.py +++ b/tests/test_ec2/test_security_groups.py @@ -9,7 +9,7 @@ from nose.tools import assert_raises import boto3 import boto from botocore.exceptions import ClientError -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError import sure # noqa from moto import mock_ec2, mock_ec2_deprecated @@ -19,9 +19,9 @@ from moto import mock_ec2, mock_ec2_deprecated def test_create_and_describe_security_group(): conn = boto.connect_ec2('the_key', 'the_secret') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: security_group = conn.create_security_group('test security group', 'this is a test security group', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateSecurityGroup operation: Request would have succeeded, but DryRun flag is set') @@ -121,9 +121,9 @@ def test_deleting_security_groups(): cm.exception.request_id.should_not.be.none # Delete by name - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.delete_security_group('test2', dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteSecurityGroup operation: Request would have succeeded, but DryRun flag is set') @@ -150,9 +150,9 @@ def test_authorize_ip_range_and_revoke(): conn = boto.connect_ec2('the_key', 'the_secret') security_group = conn.create_security_group('test', 'test') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: success = security_group.authorize(ip_protocol="tcp", from_port="22", to_port="2222", cidr_ip="123.123.123.123/32", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the GrantSecurityGroupIngress operation: Request would have succeeded, but DryRun flag is set') @@ -171,9 +171,9 @@ def test_authorize_ip_range_and_revoke(): cm.exception.request_id.should_not.be.none # Actually revoke - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: security_group.revoke(ip_protocol="tcp", from_port="22", to_port="2222", cidr_ip="123.123.123.123/32", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the RevokeSecurityGroupIngress operation: Request would have succeeded, but DryRun flag is set') @@ -185,9 +185,9 @@ def test_authorize_ip_range_and_revoke(): # Test for egress as well egress_security_group = conn.create_security_group('testegress', 'testegress', vpc_id='vpc-3432589') - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: success = conn.authorize_security_group_egress(egress_security_group.id, "tcp", from_port="22", to_port="2222", cidr_ip="123.123.123.123/32", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the GrantSecurityGroupEgress operation: Request would have succeeded, but DryRun flag is set') @@ -203,9 +203,9 @@ def test_authorize_ip_range_and_revoke(): egress_security_group.revoke.when.called_with(ip_protocol="tcp", from_port="22", to_port="2222", cidr_ip="123.123.123.122/32").should.throw(EC2ResponseError) # Actually revoke - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.revoke_security_group_egress(egress_security_group.id, "tcp", from_port="22", to_port="2222", cidr_ip="123.123.123.123/32", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the RevokeSecurityGroupEgress operation: Request would have succeeded, but DryRun flag is set') @@ -339,9 +339,9 @@ def test_security_group_tagging(): sg = conn.create_security_group("test-sg", "Test SG", vpc.id) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: sg.add_tag("Test", "Tag", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') @@ -540,11 +540,11 @@ def test_security_group_tagging_boto3(): sg = conn.create_security_group(GroupName="test-sg", Description="Test SG") - with assert_raises(JSONResponseError) as ex: + with assert_raises(ClientError) as ex: conn.create_tags(Resources=[sg['GroupId']], Tags=[{'Key': 'Test', 'Value': 'Tag'}], DryRun=True) - ex.exception.reason.should.equal('DryRunOperation') - ex.exception.status.should.equal(400) - ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') + ex.exception.response['Error']['Code'].should.equal('DryRunOperation') + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') conn.create_tags(Resources=[sg['GroupId']], Tags=[{'Key': 'Test', 'Value': 'Tag'}]) describe = conn.describe_security_groups(Filters=[{'Name': 'tag-value', 'Values': ['Tag']}]) diff --git a/tests/test_ec2/test_spot_instances.py b/tests/test_ec2/test_spot_instances.py index 1933613e..2d3cb303 100644 --- a/tests/test_ec2/test_spot_instances.py +++ b/tests/test_ec2/test_spot_instances.py @@ -4,8 +4,10 @@ import datetime import boto import boto3 +from boto.exception import EC2ResponseError +from botocore.exceptions import ClientError +import pytz import sure # noqa -from boto.exception import JSONResponseError from moto import mock_ec2, mock_ec2_deprecated from moto.backends import get_model @@ -13,98 +15,130 @@ from moto.core.utils import iso_8601_datetime_with_milliseconds @mock_ec2 -@mock_ec2_deprecated def test_request_spot_instances(): conn = boto3.client('ec2', 'us-east-1') vpc = conn.create_vpc(CidrBlock="10.0.0.0/8")['Vpc'] subnet = conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.0.0.0/16', AvailabilityZone='us-east-1a')['Subnet'] subnet_id = subnet['SubnetId'] - conn = boto.connect_ec2() + conn.create_security_group(GroupName='group1', Description='description') + conn.create_security_group(GroupName='group2', Description='description') - conn.create_security_group('group1', 'description') - conn.create_security_group('group2', 'description') + start_dt = datetime.datetime(2013, 1, 1).replace(tzinfo=pytz.utc) + end_dt = datetime.datetime(2013, 1, 2).replace(tzinfo=pytz.utc) + start = iso_8601_datetime_with_milliseconds(start_dt) + end = iso_8601_datetime_with_milliseconds(end_dt) - start = iso_8601_datetime_with_milliseconds(datetime.datetime(2013, 1, 1)) - end = iso_8601_datetime_with_milliseconds(datetime.datetime(2013, 1, 2)) - - with assert_raises(JSONResponseError) as ex: + with assert_raises(ClientError) as ex: request = conn.request_spot_instances( - price=0.5, image_id='ami-abcd1234', count=1, type='one-time', - valid_from=start, valid_until=end, launch_group="the-group", - availability_zone_group='my-group', key_name="test", - security_groups=['group1', 'group2'], user_data=b"some test data", - instance_type='m1.small', placement='us-east-1c', - kernel_id="test-kernel", ramdisk_id="test-ramdisk", - monitoring_enabled=True, subnet_id=subnet_id, dry_run=True + SpotPrice="0.5", InstanceCount=1, Type='one-time', + ValidFrom=start, ValidUntil=end, LaunchGroup="the-group", + AvailabilityZoneGroup='my-group', + LaunchSpecification={ + "ImageId": 'ami-abcd1234', + "KeyName": "test", + "SecurityGroups": ['group1', 'group2'], + "UserData": b"some test data", + "InstanceType": 'm1.small', + "Placement": { + "AvailabilityZone": 'us-east-1c', + }, + "KernelId": "test-kernel", + "RamdiskId": "test-ramdisk", + "Monitoring": { + "Enabled": True, + }, + "SubnetId": subnet_id, + }, + DryRun=True, ) - ex.exception.reason.should.equal('DryRunOperation') - ex.exception.status.should.equal(400) - ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the RequestSpotInstance operation: Request would have succeeded, but DryRun flag is set') + ex.exception.response['Error']['Code'].should.equal('DryRunOperation') + ex.exception.response['ResponseMetadata']['HTTPStatusCode'].should.equal(400) + ex.exception.response['Error']['Message'].should.equal('An error occurred (DryRunOperation) when calling the RequestSpotInstance operation: Request would have succeeded, but DryRun flag is set') request = conn.request_spot_instances( - price=0.5, image_id='ami-abcd1234', count=1, type='one-time', - valid_from=start, valid_until=end, launch_group="the-group", - availability_zone_group='my-group', key_name="test", - security_groups=['group1', 'group2'], user_data=b"some test data", - instance_type='m1.small', placement='us-east-1c', - kernel_id="test-kernel", ramdisk_id="test-ramdisk", - monitoring_enabled=True, subnet_id=subnet_id, + SpotPrice="0.5", InstanceCount=1, Type='one-time', + ValidFrom=start, ValidUntil=end, LaunchGroup="the-group", + AvailabilityZoneGroup='my-group', + LaunchSpecification={ + "ImageId": 'ami-abcd1234', + "KeyName": "test", + "SecurityGroups": ['group1', 'group2'], + "UserData": b"some test data", + "InstanceType": 'm1.small', + "Placement": { + "AvailabilityZone": 'us-east-1c', + }, + "KernelId": "test-kernel", + "RamdiskId": "test-ramdisk", + "Monitoring": { + "Enabled": True, + }, + "SubnetId": subnet_id, + }, ) - requests = conn.get_all_spot_instance_requests() + requests = conn.describe_spot_instance_requests()['SpotInstanceRequests'] requests.should.have.length_of(1) request = requests[0] - request.state.should.equal("open") - request.price.should.equal(0.5) - request.launch_specification.image_id.should.equal('ami-abcd1234') - request.type.should.equal('one-time') - request.valid_from.should.equal(start) - request.valid_until.should.equal(end) - request.launch_group.should.equal("the-group") - request.availability_zone_group.should.equal('my-group') - request.launch_specification.key_name.should.equal("test") - security_group_names = [group.name for group in request.launch_specification.groups] + request['State'].should.equal("open") + request['SpotPrice'].should.equal("0.5") + request['Type'].should.equal('one-time') + request['ValidFrom'].should.equal(start_dt) + request['ValidUntil'].should.equal(end_dt) + request['LaunchGroup'].should.equal("the-group") + request['AvailabilityZoneGroup'].should.equal('my-group') + + launch_spec = request['LaunchSpecification'] + security_group_names = [group['GroupName'] for group in launch_spec['SecurityGroups']] set(security_group_names).should.equal(set(['group1', 'group2'])) - request.launch_specification.instance_type.should.equal('m1.small') - request.launch_specification.placement.should.equal('us-east-1c') - request.launch_specification.kernel.should.equal("test-kernel") - request.launch_specification.ramdisk.should.equal("test-ramdisk") - request.launch_specification.subnet_id.should.equal(subnet_id) + + launch_spec['ImageId'].should.equal('ami-abcd1234') + launch_spec['KeyName'].should.equal("test") + launch_spec['InstanceType'].should.equal('m1.small') + launch_spec['KernelId'].should.equal("test-kernel") + launch_spec['RamdiskId'].should.equal("test-ramdisk") + launch_spec['SubnetId'].should.equal(subnet_id) -@mock_ec2_deprecated +@mock_ec2 def test_request_spot_instances_default_arguments(): """ Test that moto set the correct default arguments """ - conn = boto.connect_ec2() + conn = boto3.client('ec2', 'us-east-1') request = conn.request_spot_instances( - price=0.5, image_id='ami-abcd1234', + SpotPrice="0.5", + LaunchSpecification={ + "ImageId": 'ami-abcd1234', + } ) - requests = conn.get_all_spot_instance_requests() + requests = conn.describe_spot_instance_requests()['SpotInstanceRequests'] requests.should.have.length_of(1) request = requests[0] - request.state.should.equal("open") - request.price.should.equal(0.5) - request.launch_specification.image_id.should.equal('ami-abcd1234') - request.type.should.equal('one-time') - request.valid_from.should.equal(None) - request.valid_until.should.equal(None) - request.launch_group.should.equal(None) - request.availability_zone_group.should.equal(None) - request.launch_specification.key_name.should.equal(None) - security_group_names = [group.name for group in request.launch_specification.groups] + request['State'].should.equal("open") + request['SpotPrice'].should.equal("0.5") + request['Type'].should.equal('one-time') + request.shouldnt.contain('ValidFrom') + request.shouldnt.contain('ValidUntil') + request.shouldnt.contain('LaunchGroup') + request.shouldnt.contain('AvailabilityZoneGroup') + + launch_spec = request['LaunchSpecification'] + + security_group_names = [group['GroupName'] for group in launch_spec['SecurityGroups']] security_group_names.should.equal(["default"]) - request.launch_specification.instance_type.should.equal('m1.small') - request.launch_specification.placement.should.equal(None) - request.launch_specification.kernel.should.equal(None) - request.launch_specification.ramdisk.should.equal(None) - request.launch_specification.subnet_id.should.equal(None) + + launch_spec['ImageId'].should.equal('ami-abcd1234') + request.shouldnt.contain('KeyName') + launch_spec['InstanceType'].should.equal('m1.small') + request.shouldnt.contain('KernelId') + request.shouldnt.contain('RamdiskId') + request.shouldnt.contain('SubnetId') @mock_ec2_deprecated @@ -119,9 +153,9 @@ def test_cancel_spot_instance_request(): requests.should.have.length_of(1) - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.cancel_spot_instance_requests([requests[0].id], dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CancelSpotInstance operation: Request would have succeeded, but DryRun flag is set') @@ -148,7 +182,7 @@ def test_request_spot_instances_fulfilled(): request.state.should.equal("open") - get_model('SpotInstanceRequest')[0].state = 'active' + get_model('SpotInstanceRequest', 'us-east-1')[0].state = 'active' requests = conn.get_all_spot_instance_requests() requests.should.have.length_of(1) @@ -218,7 +252,7 @@ def test_request_spot_instances_setting_instance_id(): request = conn.request_spot_instances( price=0.5, image_id='ami-abcd1234') - req = get_model('SpotInstanceRequest')[0] + req = get_model('SpotInstanceRequest', 'us-east-1')[0] req.state = 'active' req.instance_id = 'i-12345678' diff --git a/tests/test_ec2/test_tags.py b/tests/test_ec2/test_tags.py index 1084e44c..23b7d0bd 100644 --- a/tests/test_ec2/test_tags.py +++ b/tests/test_ec2/test_tags.py @@ -4,7 +4,7 @@ from nose.tools import assert_raises import itertools import boto -from boto.exception import EC2ResponseError, JSONResponseError +from boto.exception import EC2ResponseError from boto.ec2.instance import Reservation import sure # noqa @@ -18,9 +18,9 @@ def test_add_tag(): reservation = conn.run_instances('ami-1234abcd') instance = reservation.instances[0] - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.add_tag("a key", "some value", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') @@ -45,9 +45,9 @@ def test_remove_tag(): tag.name.should.equal("a key") tag.value.should.equal("some value") - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: instance.remove_tag("a key", dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the DeleteTags operation: Request would have succeeded, but DryRun flag is set') @@ -96,9 +96,9 @@ def test_create_tags(): 'another key': 'some other value', 'blank key': ''} - with assert_raises(JSONResponseError) as ex: + with assert_raises(EC2ResponseError) as ex: conn.create_tags(instance.id, tag_dict, dry_run=True) - ex.exception.reason.should.equal('DryRunOperation') + ex.exception.error_code.should.equal('DryRunOperation') ex.exception.status.should.equal(400) ex.exception.message.should.equal('An error occurred (DryRunOperation) when calling the CreateTags operation: Request would have succeeded, but DryRun flag is set') diff --git a/tests/test_emr/test_emr.py b/tests/test_emr/test_emr.py index a24aa4bd..4b06d751 100644 --- a/tests/test_emr/test_emr.py +++ b/tests/test_emr/test_emr.py @@ -112,7 +112,7 @@ def test_describe_jobflows(): args = run_jobflow_args.copy() expected = {} - for idx in range(400): + for idx in range(4): cluster_name = 'cluster' + str(idx) args['name'] = cluster_name cluster_id = conn.run_jobflow(**args) @@ -128,7 +128,7 @@ def test_describe_jobflows(): timestamp = datetime.now(pytz.utc) time.sleep(1) - for idx in range(400, 600): + for idx in range(4, 6): cluster_name = 'cluster' + str(idx) args['name'] = cluster_name cluster_id = conn.run_jobflow(**args) @@ -139,7 +139,7 @@ def test_describe_jobflows(): 'state': 'TERMINATED' } jobs = conn.describe_jobflows() - jobs.should.have.length_of(512) + jobs.should.have.length_of(6) for cluster_id, y in expected.items(): resp = conn.describe_jobflows(jobflow_ids=[cluster_id]) @@ -147,15 +147,15 @@ def test_describe_jobflows(): resp[0].jobflowid.should.equal(cluster_id) resp = conn.describe_jobflows(states=['WAITING']) - resp.should.have.length_of(400) + resp.should.have.length_of(4) for x in resp: x.state.should.equal('WAITING') resp = conn.describe_jobflows(created_before=timestamp) - resp.should.have.length_of(400) + resp.should.have.length_of(4) resp = conn.describe_jobflows(created_after=timestamp) - resp.should.have.length_of(200) + resp.should.have.length_of(2) @mock_emr_deprecated diff --git a/tests/test_emr/test_emr_boto3.py b/tests/test_emr/test_emr_boto3.py index 1a735967..4fb5c3d7 100644 --- a/tests/test_emr/test_emr_boto3.py +++ b/tests/test_emr/test_emr_boto3.py @@ -128,7 +128,7 @@ def test_describe_job_flows(): args = deepcopy(run_job_flow_args) expected = {} - for idx in range(400): + for idx in range(4): cluster_name = 'cluster' + str(idx) args['Name'] = cluster_name cluster_id = client.run_job_flow(**args)['JobFlowId'] @@ -144,7 +144,7 @@ def test_describe_job_flows(): timestamp = datetime.now(pytz.utc) time.sleep(1) - for idx in range(400, 600): + for idx in range(4, 6): cluster_name = 'cluster' + str(idx) args['Name'] = cluster_name cluster_id = client.run_job_flow(**args)['JobFlowId'] @@ -156,7 +156,7 @@ def test_describe_job_flows(): } resp = client.describe_job_flows() - resp['JobFlows'].should.have.length_of(512) + resp['JobFlows'].should.have.length_of(6) for cluster_id, y in expected.items(): resp = client.describe_job_flows(JobFlowIds=[cluster_id]) @@ -164,15 +164,15 @@ def test_describe_job_flows(): resp['JobFlows'][0]['JobFlowId'].should.equal(cluster_id) resp = client.describe_job_flows(JobFlowStates=['WAITING']) - resp['JobFlows'].should.have.length_of(400) + resp['JobFlows'].should.have.length_of(4) for x in resp['JobFlows']: x['ExecutionStatusDetail']['State'].should.equal('WAITING') resp = client.describe_job_flows(CreatedBefore=timestamp) - resp['JobFlows'].should.have.length_of(400) + resp['JobFlows'].should.have.length_of(4) resp = client.describe_job_flows(CreatedAfter=timestamp) - resp['JobFlows'].should.have.length_of(200) + resp['JobFlows'].should.have.length_of(2) @mock_emr @@ -327,13 +327,13 @@ def test_run_job_flow(): @mock_emr def test_run_job_flow_with_invalid_params(): client = boto3.client('emr', region_name='us-east-1') - with assert_raises(ClientError) as e: + with assert_raises(ClientError) as ex: # cannot set both AmiVersion and ReleaseLabel args = deepcopy(run_job_flow_args) args['AmiVersion'] = '2.4' args['ReleaseLabel'] = 'emr-5.0.0' client.run_job_flow(**args) - e.exception.response['Error']['Code'].should.equal('ValidationException') + ex.exception.response['Error']['Message'].should.contain('ValidationException') @mock_emr diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index a51240b2..6504a548 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -201,7 +201,7 @@ def test_get_user(): def test_list_users(): path_prefix = '/' max_items = 10 - conn = boto3.client('iam') + conn = boto3.client('iam', region_name='us-east-1') conn.create_user(UserName='my-user') response = conn.list_users(PathPrefix=path_prefix, MaxItems=max_items) user = response['Users'][0] @@ -337,7 +337,7 @@ def test_managed_policy(): @mock_iam def test_boto3_create_login_profile(): - conn = boto3.client('iam') + conn = boto3.client('iam', region_name='us-east-1') with assert_raises(ClientError): conn.create_login_profile(UserName='my-user', Password='Password') diff --git a/tests/test_kinesis/test_firehose.py b/tests/test_kinesis/test_firehose.py index 14ee1916..371be253 100644 --- a/tests/test_kinesis/test_firehose.py +++ b/tests/test_kinesis/test_firehose.py @@ -4,7 +4,6 @@ import datetime from botocore.exceptions import ClientError import boto3 -from freezegun import freeze_time import sure # noqa from moto import mock_kinesis @@ -37,7 +36,6 @@ def create_stream(client, stream_name): @mock_kinesis -@freeze_time("2015-03-01") def test_create_stream(): client = boto3.client('firehose', region_name='us-east-1') @@ -48,11 +46,8 @@ def test_create_stream(): stream_description = response['DeliveryStreamDescription'] # Sure and Freezegun don't play nicely together - created = stream_description.pop('CreateTimestamp') - last_updated = stream_description.pop('LastUpdateTimestamp') - from dateutil.tz import tzlocal - assert created == datetime.datetime(2015, 3, 1, tzinfo=tzlocal()) - assert last_updated == datetime.datetime(2015, 3, 1, tzinfo=tzlocal()) + _ = stream_description.pop('CreateTimestamp') + _ = stream_description.pop('LastUpdateTimestamp') stream_description.should.equal({ 'DeliveryStreamName': 'stream1', @@ -88,7 +83,6 @@ def test_create_stream(): @mock_kinesis -@freeze_time("2015-03-01") def test_create_stream_without_redshift(): client = boto3.client('firehose', region_name='us-east-1') @@ -111,11 +105,8 @@ def test_create_stream_without_redshift(): stream_description = response['DeliveryStreamDescription'] # Sure and Freezegun don't play nicely together - created = stream_description.pop('CreateTimestamp') - last_updated = stream_description.pop('LastUpdateTimestamp') - from dateutil.tz import tzlocal - assert created == datetime.datetime(2015, 3, 1, tzinfo=tzlocal()) - assert last_updated == datetime.datetime(2015, 3, 1, tzinfo=tzlocal()) + _ = stream_description.pop('CreateTimestamp') + _ = stream_description.pop('LastUpdateTimestamp') stream_description.should.equal({ 'DeliveryStreamName': 'stream1', @@ -142,7 +133,6 @@ def test_create_stream_without_redshift(): }) @mock_kinesis -@freeze_time("2015-03-01") def test_deescribe_non_existant_stream(): client = boto3.client('firehose', region_name='us-east-1') @@ -150,7 +140,6 @@ def test_deescribe_non_existant_stream(): @mock_kinesis -@freeze_time("2015-03-01") def test_list_and_delete_stream(): client = boto3.client('firehose', region_name='us-east-1') diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py index dd68eec0..f376375a 100644 --- a/tests/test_route53/test_route53.py +++ b/tests/test_route53/test_route53.py @@ -308,7 +308,7 @@ def test_hosted_zone_private_zone_preserved(): @mock_route53 def test_hosted_zone_private_zone_preserved_boto3(): - conn = boto3.client('route53') + conn = boto3.client('route53', region_name='us-east-1') # TODO: actually create_hosted_zone statements with PrivateZone=True, but without # a _valid_ vpc-id should fail. firstzone = conn.create_hosted_zone( @@ -333,8 +333,20 @@ def test_hosted_zone_private_zone_preserved_boto3(): @mock_route53 def test_list_or_change_tags_for_resource_request(): - conn = boto3.client('route53') - healthcheck_id = str(uuid.uuid4()) + conn = boto3.client('route53', region_name='us-east-1') + health_check = conn.create_health_check( + CallerReference='foobar', + HealthCheckConfig={ + 'IPAddress': '192.0.2.44', + 'Port': 123, + 'Type': 'HTTP', + 'ResourcePath': '/', + 'RequestInterval': 30, + 'FailureThreshold': 123, + 'HealthThreshold': 123, + } + ) + healthcheck_id = health_check['HealthCheck']['Id'] tag1 = {"Key": "Deploy", "Value": "True"} tag2 = {"Key": "Name", "Value": "UnitTest"} diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 87423073..56bdfff1 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -20,17 +20,21 @@ from nose.tools import assert_raises import sure # noqa -from moto import mock_s3, mock_s3_deprecated +from moto import settings, mock_s3, mock_s3_deprecated +import moto.s3.models as s3model - -REDUCED_PART_SIZE = 256 +if settings.TEST_SERVER_MODE: + REDUCED_PART_SIZE = s3model.UPLOAD_PART_MIN_SIZE + EXPECTED_ETAG = '"140f92a6df9f9e415f74a1463bcee9bb-2"' +else: + REDUCED_PART_SIZE = 256 + EXPECTED_ETAG = '"66d1a1a2ed08fd05c137f316af4ff255-2"' def reduced_min_part_size(f): """ speed up tests by temporarily making the multipart minimum part size small """ - import moto.s3.models as s3model orig_size = s3model.UPLOAD_PART_MIN_SIZE @wraps(f) @@ -49,24 +53,23 @@ class MyModel(object): self.value = value def save(self): - conn = boto.connect_s3('the_key', 'the_secret') - bucket = conn.get_bucket('mybucket') - k = Key(bucket) - k.key = self.name - k.set_contents_from_string(self.value) + s3 = boto3.client('s3', region_name='us-east-1') + s3.put_object(Bucket='mybucket', Key=self.name, Body=self.value) -@mock_s3_deprecated +@mock_s3 def test_my_model_save(): # Create Bucket so that test can run - conn = boto.connect_s3('the_key', 'the_secret') - conn.create_bucket('mybucket') + conn = boto3.resource('s3', region_name='us-east-1') + conn.create_bucket(Bucket='mybucket') #################################### model_instance = MyModel('steve', 'is awesome') model_instance.save() - conn.get_bucket('mybucket').get_key('steve').get_contents_as_string().should.equal(b'is awesome') + body = conn.Object('mybucket', 'steve').get()['Body'].read().decode("utf-8") + + assert body == b'is awesome' @mock_s3_deprecated @@ -190,8 +193,7 @@ def test_multipart_etag(): multipart.upload_part_from_file(BytesIO(part2), 2) multipart.complete_upload() # we should get both parts as the key contents - bucket.get_key("the-key").etag.should.equal( - '"66d1a1a2ed08fd05c137f316af4ff255-2"') + bucket.get_key("the-key").etag.should.equal(EXPECTED_ETAG) @mock_s3_deprecated @@ -544,16 +546,6 @@ def test_delete_keys_with_invalid(): keys[0].name.should.equal('file1') -@mock_s3 -def test_bucket_method_not_implemented(): - requests.patch.when.called_with("https://foobar.s3.amazonaws.com/").should.throw(NotImplementedError) - - -@mock_s3 -def test_key_method_not_implemented(): - requests.post.when.called_with("https://foobar.s3.amazonaws.com/foo").should.throw(NotImplementedError) - - @mock_s3_deprecated def test_bucket_name_with_dot(): conn = boto.connect_s3() @@ -1241,7 +1233,7 @@ def test_boto3_multipart_etag(): for i, etag in enumerate(etags, 1)]}) # we should get both parts as the key contents resp = s3.get_object(Bucket='mybucket', Key='the-key') - resp['ETag'].should.equal('"66d1a1a2ed08fd05c137f316af4ff255-2"') + resp['ETag'].should.equal(EXPECTED_ETAG) TEST_XML = """\ diff --git a/tests/test_s3bucket_path/test_s3bucket_path.py b/tests/test_s3bucket_path/test_s3bucket_path.py index 24c5f7fa..528c7536 100644 --- a/tests/test_s3bucket_path/test_s3bucket_path.py +++ b/tests/test_s3bucket_path/test_s3bucket_path.py @@ -211,16 +211,6 @@ def test_post_with_metadata_to_bucket(): bucket.get_key('the-key').get_metadata('test').should.equal('metadata') -@mock_s3 -def test_bucket_method_not_implemented(): - requests.patch.when.called_with("https://s3.amazonaws.com/foobar").should.throw(NotImplementedError) - - -@mock_s3 -def test_key_method_not_implemented(): - requests.post.when.called_with("https://s3.amazonaws.com/foobar/foo").should.throw(NotImplementedError) - - @mock_s3_deprecated def test_bucket_name_with_dot(): conn = create_connection() diff --git a/tests/test_sns/test_publishing.py b/tests/test_sns/test_publishing.py index dae7e2b8..dab2a569 100644 --- a/tests/test_sns/test_publishing.py +++ b/tests/test_sns/test_publishing.py @@ -67,20 +67,3 @@ def test_publish_to_http(): response = conn.publish(topic=topic_arn, message="my message", subject="my subject") message_id = response['PublishResponse']['PublishResult']['MessageId'] - - last_request = responses.calls[-1].request - last_request.method.should.equal("POST") - parse_qs(last_request.body).should.equal({ - "Type": ["Notification"], - "MessageId": [message_id], - "TopicArn": ["arn:aws:sns:{0}:123456789012:some-topic".format(conn.region.name)], - "Subject": ["my subject"], - "Message": ["my message"], - "Timestamp": ["2013-01-01T00:00:00.000Z"], - "SignatureVersion": ["1"], - "Signature": ["EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc="], - "SigningCertURL": ["https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"], - "UnsubscribeURL": ["https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"], - }) - - diff --git a/tests/test_sns/test_publishing_boto3.py b/tests/test_sns/test_publishing_boto3.py index e31b969f..edf2948f 100644 --- a/tests/test_sns/test_publishing_boto3.py +++ b/tests/test_sns/test_publishing_boto3.py @@ -72,18 +72,3 @@ def test_publish_to_http(): response = conn.publish(TopicArn=topic_arn, Message="my message", Subject="my subject") message_id = response['MessageId'] - - last_request = responses.calls[-2].request - last_request.method.should.equal("POST") - parse_qs(last_request.body).should.equal({ - "Type": ["Notification"], - "MessageId": [message_id], - "TopicArn": ["arn:aws:sns:{0}:123456789012:some-topic".format(conn._client_config.region_name)], - "Subject": ["my subject"], - "Message": ["my message"], - "Timestamp": ["2013-01-01T00:00:00.000Z"], - "SignatureVersion": ["1"], - "Signature": ["EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc="], - "SigningCertURL": ["https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem"], - "UnsubscribeURL": ["https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"], - }) diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py index fd496c21..89ea7413 100644 --- a/tests/test_sqs/test_sqs.py +++ b/tests/test_sqs/test_sqs.py @@ -408,11 +408,6 @@ def test_delete_batch_operation(): queue.count().should.equal(1) -@mock_sqs -def test_sqs_method_not_implemented(): - requests.post.when.called_with("https://sqs.amazonaws.com/?Action=[foobar]").should.throw(NotImplementedError) - - @mock_sqs_deprecated def test_queue_attributes(): conn = boto.connect_sqs('the_key', 'the_secret') diff --git a/tests/test_sts/test_sts.py b/tests/test_sts/test_sts.py index 870f1486..19865ca7 100644 --- a/tests/test_sts/test_sts.py +++ b/tests/test_sts/test_sts.py @@ -68,7 +68,7 @@ def test_assume_role(): @mock_sts def test_get_caller_identity(): - identity = boto3.client("sts").get_caller_identity() + identity = boto3.client("sts", region_name='us-east-1').get_caller_identity() identity['Arn'].should.equal('arn:aws:sts::123456789012:user/moto') identity['UserId'].should.equal('AKIAIOSFODNN7EXAMPLE') diff --git a/tests/test_swf/utils.py b/tests/test_swf/utils.py index 2df0fcc9..756d17c2 100644 --- a/tests/test_swf/utils.py +++ b/tests/test_swf/utils.py @@ -1,6 +1,5 @@ import boto -from moto import mock_swf from moto.swf.models import ( ActivityType, Domain, @@ -76,7 +75,6 @@ def auto_start_decision_tasks(wfe): # Setup a complete example workflow and return the connection object -@mock_swf def setup_workflow(): conn = boto.connect_swf("the_key", "the_secret") conn.register_domain("test-domain", "60", description="A test domain") diff --git a/tox.ini b/tox.ini index 368eba9c..3fe5d014 100644 --- a/tox.ini +++ b/tox.ini @@ -11,3 +11,4 @@ commands = [flake8] ignore = E128,E501 +exclude = moto/packages,dist