Save updated-in-place resources to backup stack

The patch changes the approach of resource backup during stack
update. It stores the definition of resource that needs to be
updated in-place to backup stack(if it was not created before).

Without this functionality, InvalidTemplateReference will be thrown
every time when update-replaced resource has a reference to updated
in-place resource and stack update was failed. In this case, backup
stack has a resource that refers to not-presented another resource
and backup stack deletion generates an exception.

Change-Id: I436c44a579bb4df3031d1a17b9ca5b62da37afeb
Closes-bug: #1446575
This commit is contained in:
kairat_kushaev 2015-05-05 19:22:32 +03:00
parent 1ad9f9f186
commit f2edd0d68e
2 changed files with 77 additions and 0 deletions

View File

@ -135,6 +135,15 @@ class StackUpdate(object):
except resource.UpdateReplace:
pass
else:
# Save resource definition to backup stack if it is not
# present in backup stack template already
if res_name not in self.previous_stack.t[
self.previous_stack.t.RESOURCES]:
definition = existing_res.t.reparse(self.previous_stack,
existing_res.stack.t)
self.previous_stack.t.add_resource(definition)
self.previous_stack.t.store(self.previous_stack.context)
LOG.info(_LI("Resource %(res_name)s for stack %(stack_name)s "
"updated"),
{'res_name': res_name,

View File

@ -1535,3 +1535,71 @@ class StackUpdateTest(common.HeatTestCase):
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('foo', self.stack['AResource'].properties['Foo'])
def test_delete_stack_when_update_failed_twice(self):
"""Test when stack update failed twice and delete the stack.
Test checks the following scenario:
1. Create stack
2. Update stack (failed)
3. Update stack (failed)
4. Delete stack
The test checks the behavior of backup stack when update is failed.
If some resources were not backed up correctly then test will fail.
"""
tmpl_create = {
'heat_template_version': '2013-05-23',
'resources': {
'Ares': {'type': 'GenericResourceType'}
}
}
# create a stack
self.stack = stack.Stack(self.ctx, 'update_fail_test_stack',
template.Template(tmpl_create),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
tmpl_update = {
'heat_template_version': '2013-05-23',
'resources': {
'Ares': {'type': 'GenericResourceType'},
'Bres': {'type': 'GenericResourceType'},
'Cres': {
'type': 'ResourceWithPropsType',
'properties': {
'Foo': {'get_resource': 'Bres'},
}
}
}
}
mock_create = self.patchobject(
generic_rsrc.ResourceWithProps,
'handle_create',
side_effect=[Exception, Exception])
updated_stack_first = stack.Stack(self.ctx,
'update_fail_test_stack',
template.Template(tmpl_update))
self.stack.update(updated_stack_first)
self.stack.resources['Cres'].resource_id_set('c_res')
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
# try to update the stack again
updated_stack_second = stack.Stack(self.ctx,
'update_fail_test_stack',
template.Template(tmpl_update))
self.stack.update(updated_stack_second)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.assertEqual(mock_create.call_count, 2)
# delete the failed stack
self.stack.delete()
self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
self.stack.state)