From 734f777da48cc008d8da4bf197c7a87a03be8846 Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Mon, 2 Feb 2015 13:12:48 +1000 Subject: [PATCH] Use "if stack is not None" and not "if stack" Problem: If a nested stack has no resources, then we do not get a "nested" link in the resource information returned by our API. Why? In engine/api.py we have: if (hasattr(resource, 'nested') and callable(resource.nested) and resource.nested()): res[rpc_api.RES_NESTED_STACK_ID] = dict(resource.nested().identifier()) The problem with this is the python definition of False, from the docs: "instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False. [1]" So if you have a stack with zero resources (len() returns 0), the stack will be False :-O Solution: Use "if stack is not None:" instead of "if stack". Change-Id: Ibdd1cb5dee6ce8e58f7d8f2586a495caded79134 Closes-bug: 1416917 --- heat/engine/api.py | 2 +- heat/engine/resources/template_resource.py | 4 ++-- heat/tests/test_engine_api_utils.py | 12 ++++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/heat/engine/api.py b/heat/engine/api.py index 9a9b63b548..df6a8a956c 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -177,7 +177,7 @@ def format_stack_resource(resource, detail=True, with_props=False, } if (hasattr(resource, 'nested') and callable(resource.nested) and - resource.nested()): + resource.nested() is not None): res[rpc_api.RES_NESTED_STACK_ID] = dict(resource.nested().identifier()) if resource.stack.parent_resource: diff --git a/heat/engine/resources/template_resource.py b/heat/engine/resources/template_resource.py index 817bade5e4..2dfe07a997 100644 --- a/heat/engine/resources/template_resource.py +++ b/heat/engine/resources/template_resource.py @@ -266,7 +266,7 @@ class TemplateResource(stack_resource.StackResource): return self.delete_nested() def FnGetRefId(self): - if not self.nested(): + if self.nested() is None: return six.text_type(self.name) if 'OS::stack_id' in self.nested().outputs: @@ -280,7 +280,7 @@ class TemplateResource(stack_resource.StackResource): return None def _get_inner_resource(resource_name): - if self.nested(): + if self.nested() is not None: try: return self.nested()[resource_name] except KeyError: diff --git a/heat/tests/test_engine_api_utils.py b/heat/tests/test_engine_api_utils.py index 44ca3b07a9..162d75f04a 100644 --- a/heat/tests/test_engine_api_utils.py +++ b/heat/tests/test_engine_api_utils.py @@ -207,6 +207,18 @@ class FormatTest(common.HeatTestCase): formatted = api.format_stack_resource(res, False) self.assertEqual(resource_keys, set(formatted.keys())) + def test_format_stack_resource_with_nested_stack_empty(self): + res = self.stack['generic1'] + nested_id = {'foo': 'bar'} + + res.nested = mock.MagicMock() + res.nested.return_value.identifier.return_value = nested_id + res.nested.return_value.__len__.return_value = 0 + + formatted = api.format_stack_resource(res, False) + res.nested.return_value.identifier.assert_called_once_with() + self.assertEqual(nested_id, formatted[rpc_api.RES_NESTED_STACK_ID]) + def test_format_stack_resource_required_by(self): res1 = api.format_stack_resource(self.stack['generic1']) res2 = api.format_stack_resource(self.stack['generic2'])