From c3f9337c11b78f98704dce9e76e2b74e9098670a Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Wed, 22 Jul 2015 10:56:55 +1200 Subject: [PATCH] Catch NotFound in format_stack_resource for nested() If the underlying nested stack doesn't exist for whatever reason, calling heat resource-list results in a NotFound response even though heat stack-show confirms the stack exists. This change catches NotFound when calling resource.nested() in format_stack_resource. Change-Id: Iba92d0590561dffb8f902865cc7134b2eddc23ac Closes-Bug: #1476834 --- heat/engine/api.py | 11 +++-- heat/tests/test_engine_api_utils.py | 23 +++++++++++ .../functional/test_resource_group.py | 40 +++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/heat/engine/api.py b/heat/engine/api.py index 47bffd5ad..46b9b5beb 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -17,6 +17,7 @@ from oslo_log import log as logging from oslo_utils import timeutils import six +from heat.common import exception from heat.common.i18n import _ from heat.common.i18n import _LE from heat.common import param_utils @@ -200,9 +201,13 @@ def format_stack_resource(resource, detail=True, with_props=False, rpc_api.RES_REQUIRED_BY: resource.required_by(), } - if (hasattr(resource, 'nested') and callable(resource.nested) and - resource.nested() is not None): - res[rpc_api.RES_NESTED_STACK_ID] = dict(resource.nested().identifier()) + try: + if (hasattr(resource, 'nested') and callable(resource.nested) and + resource.nested() is not None): + res[rpc_api.RES_NESTED_STACK_ID] = dict( + resource.nested().identifier()) + except exception.NotFound: + pass if resource.stack.parent_resource_name: res[rpc_api.RES_PARENT_RESOURCE] = resource.stack.parent_resource_name diff --git a/heat/tests/test_engine_api_utils.py b/heat/tests/test_engine_api_utils.py index 39e51c010..b03c68e65 100644 --- a/heat/tests/test_engine_api_utils.py +++ b/heat/tests/test_engine_api_utils.py @@ -19,6 +19,7 @@ import mock from oslo_utils import timeutils import six +from heat.common import exception from heat.common import identifier from heat.common import template_format from heat.engine import api @@ -205,6 +206,28 @@ class FormatTest(common.HeatTestCase): formatted = api.format_stack_resource(res, False) self.assertEqual(resource_keys, set(six.iterkeys(formatted))) + def test_format_stack_resource_with_nested_stack_not_found(self): + res = self.stack['generic1'] + res.nested = mock.Mock() + res.nested.side_effect = exception.NotFound() + + resource_keys = set(( + rpc_api.RES_CREATION_TIME, + rpc_api.RES_UPDATED_TIME, + rpc_api.RES_NAME, + rpc_api.RES_PHYSICAL_ID, + rpc_api.RES_ACTION, + rpc_api.RES_STATUS, + rpc_api.RES_STATUS_DATA, + rpc_api.RES_TYPE, + rpc_api.RES_ID, + rpc_api.RES_STACK_ID, + rpc_api.RES_STACK_NAME, + rpc_api.RES_REQUIRED_BY)) + + formatted = api.format_stack_resource(res, False) + self.assertEqual(resource_keys, set(six.iterkeys(formatted))) + def test_format_stack_resource_with_nested_stack_empty(self): res = self.stack['generic1'] nested_id = {'foo': 'bar'} diff --git a/heat_integrationtests/functional/test_resource_group.py b/heat_integrationtests/functional/test_resource_group.py index a41f84106..ab3b355b3 100644 --- a/heat_integrationtests/functional/test_resource_group.py +++ b/heat_integrationtests/functional/test_resource_group.py @@ -472,3 +472,43 @@ outputs: stack = self.client.stacks.get(stack_identifier) self.assertEqual('goopie', self._stack_output(stack, 'test0')) self.assertEqual('different', self._stack_output(stack, 'test1')) + + +class ResourceGroupErrorResourceTest(test.HeatIntegrationTest): + template = ''' +heat_template_version: "2013-05-23" +resources: + group1: + type: OS::Heat::ResourceGroup + properties: + count: 2 + resource_def: + type: fail.yaml +''' + nested_templ = ''' +heat_template_version: "2013-05-23" +resources: + oops: + type: OS::Heat::TestResource + properties: + fail: true + wait_secs: 2 +''' + + def setUp(self): + super(ResourceGroupErrorResourceTest, self).setUp() + self.client = self.orchestration_client + + def test_fail(self): + stack_identifier = self.stack_create( + template=self.template, + files={'fail.yaml': self.nested_templ}, + expected_status='CREATE_FAILED', + enable_cleanup=False) + stack = self.client.stacks.get(stack_identifier) + + self.assertEqual('CREATE_FAILED', stack.stack_status) + self.client.stacks.delete(stack_identifier) + self._wait_for_stack_status( + stack_identifier, 'DELETE_COMPLETE', + success_on_not_found=True)