Fallback to db for resource show

This change adds a Stack.resource_get which is used by
describe_stack_resource (stack resource show). It has a different
behaviour when the resource is missing from the stack resources dict,
attempting to return a resource from the DB record in the current
stack.

This allows "stack resource show" to show a resource in a convergence
stack while the resource is being deleted.

Change-Id: I975778ac36ae31fe5bd3ebdbcd7c48a6fcc60757
Partial-Bug: #1301320
Related-Bug: #1523748
This commit is contained in:
Steve Baker 2016-07-15 16:24:13 +12:00
parent e17b4c1750
commit 10030beb2f
3 changed files with 66 additions and 3 deletions

View File

@ -1732,12 +1732,12 @@ class EngineService(service.Service):
LOG.warning(_LW("Access denied to resource %s"), resource_name)
raise exception.Forbidden()
if resource_name not in stack:
resource = stack.resource_get(resource_name)
if not resource:
raise exception.ResourceNotFound(resource_name=resource_name,
stack_name=stack.name)
return api.format_stack_resource(stack[resource_name],
with_attr=with_attr)
return api.format_stack_resource(resource, with_attr=with_attr)
@context.request_context
def resource_signal(self, cnxt, stack_identity, resource_name, details,

View File

@ -358,6 +358,29 @@ class Stack(collections.Mapping):
self._db_resources = _db_resources
return self._db_resources.get(name)
def _resource_from_db_resource(self, db_res):
tid = db_res.current_template_id
if tid == self.t.id:
t = self.t
else:
t = tmpl.Template.load(self.context, tid)
res_defn = t.resource_definitions(self)[db_res.name]
return resource.Resource(db_res.name, res_defn, self)
def resource_get(self, name):
"""Return a stack resource, even if not in the current template."""
res = self.resources.get(name)
if res:
return res
# fall back to getting the resource from the database
db_res = self.db_resource_get(name)
if db_res:
return self._resource_from_db_resource(db_res)
return None
@property
def dependencies(self):
if self._dependencies is None:

View File

@ -224,6 +224,46 @@ class StackTest(common.HeatTestCase):
self.assertEqual(1, self.stack.total_resources(self.stack.id))
self.assertEqual(1, self.stack.total_resources())
def test_resource_get(self):
tpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources':
{'A': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'test_stack',
template.Template(tpl),
status_reason='blarg')
self.stack.store()
self.assertEqual('A', self.stack.resource_get('A').name)
self.assertEqual(self.stack['A'], self.stack.resource_get('A'))
self.assertIsNone(self.stack.resource_get('B'))
@mock.patch.object(resource_objects.Resource, 'get_all_by_stack')
def test_resource_get_db_fallback(self, gabs):
tpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources':
{'A': {'Type': 'GenericResourceType'}}}
self.stack = stack.Stack(self.ctx, 'test_stack',
template.Template(tpl),
status_reason='blarg')
self.stack.store()
tpl2 = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources':
{'A': {'Type': 'GenericResourceType'},
'B': {'Type': 'GenericResourceType'}}}
t2 = template.Template(tpl2)
t2.store(self.ctx)
db_resources = {
'A': mock.MagicMock(),
'B': mock.MagicMock(current_template_id=t2.id)
}
db_resources['A'].name = 'A'
db_resources['B'].name = 'B'
gabs.return_value = db_resources
self.assertEqual('A', self.stack.resource_get('A').name)
self.assertEqual('B', self.stack.resource_get('B').name)
self.assertIsNone(self.stack.resource_get('C'))
def test_iter_resources_with_nested(self):
tpl = {'HeatTemplateFormatVersion': '2012-12-12',
'Resources':