From 773a49c40d02535e73dbc50081920a42d87d3219 Mon Sep 17 00:00:00 2001 From: Declan Shanaghy Date: Fri, 3 Jun 2016 16:30:34 -0700 Subject: [PATCH 1/2] Fix typo in message variable --- moto/cloudformation/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/cloudformation/exceptions.py b/moto/cloudformation/exceptions.py index de672d8b..4bbbbbcf 100644 --- a/moto/cloudformation/exceptions.py +++ b/moto/cloudformation/exceptions.py @@ -11,7 +11,7 @@ class UnformattedGetAttTemplateException(Exception): class ValidationError(BadRequest): def __init__(self, name_or_id, message=None): if message is None: - messgae="Stack:{0} does not exist".format(name_or_id), + message="Stack:{0} does not exist".format(name_or_id), template = Template(ERROR_RESPONSE) super(ValidationError, self).__init__() From d8baa957e917bcb559bbac70b5884b5a6177923f Mon Sep 17 00:00:00 2001 From: Hitesh Ghia Date: Tue, 7 Jun 2016 13:31:42 -0700 Subject: [PATCH 2/2] Fix cfn delete stack functionality --- moto/cloudformation/parsing.py | 15 +++++----- .../test_cloudformation_stack_crud.py | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index c823bae2..f569f6a8 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -403,19 +403,18 @@ class ResourceMap(collections.Mapping): while remaining_resources and tries < 5: for resource in remaining_resources.copy(): parsed_resource = self._parsed_resources.get(resource) - if parsed_resource: - try: + try: + if parsed_resource and hasattr(parsed_resource, 'delete'): parsed_resource.delete(self._region_name) - except Exception as e: - # skip over dependency violations, and try again in a second pass - last_exception = e - else: - remaining_resources.remove(resource) + except Exception as e: + # skip over dependency violations, and try again in a second pass + last_exception = e + else: + remaining_resources.remove(resource) tries += 1 if tries == 5: raise last_exception - class OutputMap(collections.Mapping): def __init__(self, resources, template): self._template = template diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index e516d92d..bbf65fae 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -27,8 +27,23 @@ dummy_template2 = { "Resources": {}, } +# template with resource which has no delete attribute defined +dummy_template3 = { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Stack 3", + "Resources": { + "VPC": { + "Properties": { + "CidrBlock": "192.168.0.0/16", + }, + "Type": "AWS::EC2::VPC" + } + }, +} + dummy_template_json = json.dumps(dummy_template) dummy_template_json2 = json.dumps(dummy_template2) +dummy_template_json3 = json.dumps(dummy_template3) @mock_cloudformation @@ -220,6 +235,19 @@ def test_delete_stack_by_id(): conn.describe_stacks(stack_id).should.have.length_of(1) +@mock_cloudformation +def test_delete_stack_with_resource_missing_delete_attr(): + conn = boto.connect_cloudformation() + conn.create_stack( + "test_stack", + template_body=dummy_template_json3, + ) + + conn.list_stacks().should.have.length_of(1) + conn.delete_stack("test_stack") + conn.list_stacks().should.have.length_of(0) + + @mock_cloudformation def test_bad_describe_stack(): conn = boto.connect_cloudformation()