From 94e969fed5de7b298d19a23a68d4c52081a965e8 Mon Sep 17 00:00:00 2001 From: Joseph Lawson Date: Wed, 22 Oct 2014 23:58:42 -0400 Subject: [PATCH] Enhance DescribeStacks. Keep track of deleted stacks. Stack status. Made describe_stacks more in line with http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStackResource.html --- moto/cloudformation/models.py | 17 ++++++++-- moto/cloudformation/responses.py | 12 ++++--- .../test_cloudformation_stack_crud.py | 31 +++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index d4420cafd..f54182174 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -12,6 +12,7 @@ class FakeStack(object): self.stack_id = stack_id self.name = name self.template = template + self.status = 'CREATE_COMPLETE' template_dict = json.loads(self.template) self.description = template_dict.get('Description') @@ -35,6 +36,7 @@ class CloudFormationBackend(BaseBackend): def __init__(self): self.stacks = {} + self.deleted_stacks = {} def create_stack(self, name, template): stack_id = generate_stack_id(name) @@ -42,10 +44,16 @@ class CloudFormationBackend(BaseBackend): self.stacks[stack_id] = new_stack return new_stack - def describe_stacks(self, names): + def describe_stacks(self, name_or_stack_id): stacks = self.stacks.values() - if names: - return [stack for stack in stacks if stack.name in names] + if name_or_stack_id: + for stack in stacks: + if stack.name == name_or_stack_id or stack.stack_id == name_or_stack_id: + return [stack] + deleted_stacks = self.deleted_stacks.values() + for stack in deleted_stacks: + if stack.stack_id == name_or_stack_id: + return [stack] else: return stacks @@ -68,6 +76,9 @@ class CloudFormationBackend(BaseBackend): def delete_stack(self, name_or_stack_id): if name_or_stack_id in self.stacks: # Delete by stack id + stack = self.stacks.pop(name_or_stack_id, None) + stack.status = 'DELETE_COMPLETE' + self.deleted_stacks[stack.stack_id] = stack return self.stacks.pop(name_or_stack_id, None) else: # Delete by stack name diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index 7d6690323..4267a6e48 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -27,8 +27,10 @@ class CloudFormationResponse(BaseResponse): return json.dumps(stack_body) def describe_stacks(self): - names = [value[0] for key, value in self.querystring.items() if "StackName" in key] - stacks = cloudformation_backend.describe_stacks(names) + stack_name_or_id = None + if self._get_param('StackName'): + stack_name_or_id = self.querystring.get('StackName')[0] + stacks = cloudformation_backend.describe_stacks(stack_name_or_id) template = Template(DESCRIBE_STACKS_TEMPLATE) return template.render(stacks=stacks) @@ -86,7 +88,7 @@ DESCRIBE_STACKS_TEMPLATE = """ {{ stack.name }} {{ stack.stack_id }} 2010-07-27T22:28:28Z - CREATE_COMPLETE + {{ stack.status }} false {% for output in stack.stack_outputs %} @@ -108,7 +110,7 @@ LIST_STACKS_RESPONSE = """ {% for stack in stacks %} {{ stack.id }} - CREATE_IN_PROGRESS + {{ stack.status }} {{ stack.name }} 2011-05-23T15:47:44Z {{ stack.description }} @@ -129,7 +131,7 @@ LIST_STACKS_RESOURCES_RESPONSE = """ {{ resource.physical_resource_id }} {{ resource.type }} 2010-07-27T22:27:28Z - CREATE_COMPLETE + {{ stack.status }} {% endfor %} diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index fdbe7481b..f818e42ef 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -47,6 +47,37 @@ def test_describe_stack_by_name(): stack.stack_name.should.equal('test_stack') +@mock_cloudformation +def test_describe_stack_by_stack_id(): + conn = boto.connect_cloudformation() + conn.create_stack( + "test_stack", + template_body=dummy_template_json, + ) + + stack = conn.describe_stacks("test_stack")[0] + stack_by_id = conn.describe_stacks(stack.stack_id)[0] + stack_by_id.stack_id.should.equal(stack.stack_id) + stack_by_id.stack_name.should.equal("test_stack") + + +@mock_cloudformation +def test_describe_deleted_stack(): + conn = boto.connect_cloudformation() + conn.create_stack( + "test_stack", + template_body=dummy_template_json, + ) + + stack = conn.describe_stacks("test_stack")[0] + stack_id = stack.stack_id + conn.delete_stack(stack.stack_id) + stack_by_id = conn.describe_stacks(stack_id)[0] + stack_by_id.stack_id.should.equal(stack.stack_id) + stack_by_id.stack_name.should.equal("test_stack") + stack_by_id.stack_status.should.equal("DELETE_COMPLETE") + + @mock_cloudformation def test_get_template_by_name(): conn = boto.connect_cloudformation()