diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index 864e98a92..242cca1fc 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -85,9 +85,9 @@ class FakeStack(BaseModel): def _parse_template(self): yaml.add_multi_constructor('', yaml_tag_constructor) try: - self.template_dict = yaml.load(self.template) + self.template_dict = yaml.load(self.template, Loader=yaml.Loader) except yaml.parser.ParserError: - self.template_dict = json.loads(self.template) + self.template_dict = json.loads(self.template, Loader=yaml.Loader) @property def stack_parameters(self): diff --git a/moto/ecs/models.py b/moto/ecs/models.py index 4a6737ceb..c9fdb9808 100644 --- a/moto/ecs/models.py +++ b/moto/ecs/models.py @@ -358,6 +358,20 @@ class ContainerInstance(BaseObject): return formatted_attr +class ClusterFailure(BaseObject): + def __init__(self, reason, cluster_name): + self.reason = reason + self.arn = "arn:aws:ecs:us-east-1:012345678910:cluster/{0}".format( + cluster_name) + + @property + def response_object(self): + response_object = self.gen_response_object() + response_object['reason'] = self.reason + response_object['arn'] = self.arn + return response_object + + class ContainerInstanceFailure(BaseObject): def __init__(self, reason, container_instance_id): @@ -419,6 +433,7 @@ class EC2ContainerServiceBackend(BaseBackend): def describe_clusters(self, list_clusters_name=None): list_clusters = [] + failures = [] if list_clusters_name is None: if 'default' in self.clusters: list_clusters.append(self.clusters['default'].response_object) @@ -429,9 +444,8 @@ class EC2ContainerServiceBackend(BaseBackend): list_clusters.append( self.clusters[cluster_name].response_object) else: - raise Exception( - "{0} is not a cluster".format(cluster_name)) - return list_clusters + failures.append(ClusterFailure('MISSING', cluster_name)) + return list_clusters, failures def delete_cluster(self, cluster_str): cluster_name = cluster_str.split('/')[-1] diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py index e0bfefc02..964ef59d2 100644 --- a/moto/ecs/responses.py +++ b/moto/ecs/responses.py @@ -45,10 +45,10 @@ class EC2ContainerServiceResponse(BaseResponse): def describe_clusters(self): list_clusters_name = self._get_param('clusters') - clusters = self.ecs_backend.describe_clusters(list_clusters_name) + clusters, failures = self.ecs_backend.describe_clusters(list_clusters_name) return json.dumps({ 'clusters': clusters, - 'failures': [] + 'failures': [cluster.response_object for cluster in failures] }) def delete_cluster(self): diff --git a/tests/test_cloudformation/test_stack_parsing.py b/tests/test_cloudformation/test_stack_parsing.py index d25c69cf1..d21db2d48 100644 --- a/tests/test_cloudformation/test_stack_parsing.py +++ b/tests/test_cloudformation/test_stack_parsing.py @@ -448,8 +448,8 @@ def test_short_form_func_in_yaml_teamplate(): KeySplit: !Split [A, B] KeySub: !Sub A """ - yaml.add_multi_constructor('', yaml_tag_constructor) - template_dict = yaml.load(template) + yaml.add_multi_constructor('', yaml_tag_constructor, Loader=yaml.Loader) + template_dict = yaml.load(template, Loader=yaml.Loader) key_and_expects = [ ['KeyRef', {'Ref': 'foo'}], ['KeyB64', {'Fn::Base64': 'valueToEncode'}], diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py index a0d470935..3bf25b8fc 100644 --- a/tests/test_ecs/test_ecs_boto3.py +++ b/tests/test_ecs/test_ecs_boto3.py @@ -47,6 +47,15 @@ def test_list_clusters(): 'arn:aws:ecs:us-east-1:012345678910:cluster/test_cluster1') +@mock_ecs +def test_describe_clusters(): + client = boto3.client('ecs', region_name='us-east-1') + response = client.describe_clusters(clusters=["some-cluster"]) + response['failures'].should.contain({ + 'arn': 'arn:aws:ecs:us-east-1:012345678910:cluster/some-cluster', + 'reason': 'MISSING' + }) + @mock_ecs def test_delete_cluster(): client = boto3.client('ecs', region_name='us-east-1') diff --git a/tests/test_kms/test_kms.py b/tests/test_kms/test_kms.py index 0f7bab4cd..95e39a0e7 100644 --- a/tests/test_kms/test_kms.py +++ b/tests/test_kms/test_kms.py @@ -8,8 +8,8 @@ import sure # noqa from moto import mock_kms, mock_kms_deprecated from nose.tools import assert_raises from freezegun import freeze_time -from datetime import datetime, timedelta -from dateutil.tz import tzlocal +from datetime import datetime +from dateutil.tz import tzutc @mock_kms_deprecated @@ -660,7 +660,7 @@ def test_schedule_key_deletion(): KeyId=key['KeyMetadata']['KeyId'] ) assert response['KeyId'] == key['KeyMetadata']['KeyId'] - assert response['DeletionDate'] == datetime(2015, 1, 31, 12, 0, tzinfo=tzlocal()) + assert response['DeletionDate'] == datetime(2015, 1, 31, 12, 0, tzinfo=tzutc()) else: # Can't manipulate time in server mode response = client.schedule_key_deletion( @@ -685,7 +685,7 @@ def test_schedule_key_deletion_custom(): PendingWindowInDays=7 ) assert response['KeyId'] == key['KeyMetadata']['KeyId'] - assert response['DeletionDate'] == datetime(2015, 1, 8, 12, 0, tzinfo=tzlocal()) + assert response['DeletionDate'] == datetime(2015, 1, 8, 12, 0, tzinfo=tzutc()) else: # Can't manipulate time in server mode response = client.schedule_key_deletion( diff --git a/tests/test_s3/test_server.py b/tests/test_s3/test_server.py index 9c8252a04..b179a2329 100644 --- a/tests/test_s3/test_server.py +++ b/tests/test_s3/test_server.py @@ -15,6 +15,7 @@ class AuthenticatedClient(FlaskClient): def open(self, *args, **kwargs): kwargs['headers'] = kwargs.get('headers', {}) kwargs['headers']['Authorization'] = "Any authorization header" + kwargs['content_length'] = 0 # Fixes content-length complaints. return super(AuthenticatedClient, self).open(*args, **kwargs) diff --git a/tests/test_s3bucket_path/test_bucket_path_server.py b/tests/test_s3bucket_path/test_bucket_path_server.py index 434110e87..f6238dd28 100644 --- a/tests/test_s3bucket_path/test_bucket_path_server.py +++ b/tests/test_s3bucket_path/test_bucket_path_server.py @@ -13,6 +13,7 @@ class AuthenticatedClient(FlaskClient): def open(self, *args, **kwargs): kwargs['headers'] = kwargs.get('headers', {}) kwargs['headers']['Authorization'] = "Any authorization header" + kwargs['content_length'] = 0 # Fixes content-length complaints. return super(AuthenticatedClient, self).open(*args, **kwargs)