diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index 4e3027f8b..618e736e0 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -91,6 +91,10 @@ class CloudFormationBackend(BaseBackend): # stack.template = template # return stack + def list_stack_resources(self, stack_name_or_id): + stack = self.get_stack(stack_name_or_id) + return stack.stack_resources + def delete_stack(self, name_or_stack_id): if name_or_stack_id in self.stacks: # Delete by stack id diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index 18c1d23f3..d0ee90138 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -67,7 +67,7 @@ class CloudFormationResponse(BaseResponse): stack_name = self._get_param('StackName') stack = self.cloudformation_backend.get_stack(stack_name) - template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE) + template = self.response_template(DESCRIBE_STACKS_RESOURCES_RESPONSE) return template.render(stack=stack) def list_stacks(self): @@ -75,6 +75,13 @@ class CloudFormationResponse(BaseResponse): template = self.response_template(LIST_STACKS_RESPONSE) return template.render(stacks=stacks) + def list_stack_resources(self): + stack_name_or_id = self._get_param('StackName') + resources = self.cloudformation_backend.list_stack_resources(stack_name_or_id) + + template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE) + return template.render(resources=resources) + def get_template(self): name_or_stack_id = self.querystring.get('StackName')[0] @@ -166,7 +173,7 @@ LIST_STACKS_RESPONSE = """ """ -LIST_STACKS_RESOURCES_RESPONSE = """ +DESCRIBE_STACKS_RESOURCES_RESPONSE = """ {% for resource in stack.stack_resources %} @@ -181,3 +188,23 @@ LIST_STACKS_RESOURCES_RESPONSE = """ {% endfor %} """ + + +LIST_STACKS_RESOURCES_RESPONSE = """ + + + {% for resource in resources %} + + CREATE_COMPLETE + {{ resource.logical_resource_id }} + 2011-06-21T20:15:58Z + {{ resource.physical_resource_id }} + {{ resource.type }} + + {% endfor %} + + + + 2d06e36c-ac1d-11e0-a958-f9382b6eb86b + +""" diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 7db6c22e4..f06acf30b 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -68,6 +68,37 @@ def test_stack_sqs_integration(): queue.physical_resource_id.should.equal("my-queue") +@mock_cloudformation() +def test_stack_list_resources(): + sqs_template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "QueueGroup": { + + "Type": "AWS::SQS::Queue", + "Properties": { + "QueueName": "my-queue", + "VisibilityTimeout": 60, + } + }, + }, + } + sqs_template_json = json.dumps(sqs_template) + + conn = boto.cloudformation.connect_to_region("us-west-1") + conn.create_stack( + "test_stack", + template_body=sqs_template_json, + ) + + resources = conn.list_stack_resources("test_stack") + assert len(resources) == 1 + queue = resources[0] + queue.resource_type.should.equal('AWS::SQS::Queue') + queue.logical_resource_id.should.equal("QueueGroup") + queue.physical_resource_id.should.equal("my-queue") + + @mock_ec2() @mock_cloudformation() def test_stack_ec2_integration(): @@ -1198,17 +1229,17 @@ def test_subnets_should_be_created_with_availability_zone(): vpc = vpc_conn.create_vpc("10.0.0.0/16") subnet_template = { - "AWSTemplateFormatVersion" : "2010-09-09", - "Resources" : { - "testSubnet" : { - "Type" : "AWS::EC2::Subnet", - "Properties" : { - "VpcId" : vpc.id, - "CidrBlock" : "10.0.0.0/24", - "AvailabilityZone" : "us-west-1b", - } - } - } + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "testSubnet": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "VpcId": vpc.id, + "CidrBlock": "10.0.0.0/24", + "AvailabilityZone": "us-west-1b", + } + } + } } cf_conn = boto.cloudformation.connect_to_region("us-west-1") template_json = json.dumps(subnet_template)