Fix stack-list after failed update

When a update is being performed, the existing template is updated
incrementally as resources change. Now if update fails, template will
have mix of resources from existing and new template. Hence we need to
ensure we have the user params of both.

Change-Id: I9e6831c58b167809e3faed519d2fb4f7c61244fc
Closes-Bug: #1477812
This commit is contained in:
Rakesh H S 2015-07-28 14:11:41 +05:30
parent a21614438c
commit 2aebdf7f3f
2 changed files with 111 additions and 0 deletions

View File

@ -25,6 +25,7 @@ from osprofiler import profiler
import six
from heat.common import context as common_context
from heat.common import environment_format as env_fmt
from heat.common import exception
from heat.common.i18n import _
from heat.common.i18n import _LE
@ -1131,6 +1132,8 @@ class Stack(collections.Mapping):
**kwargs)
backup_stack = self._backup_stack()
existing_params = environment.Environment({env_fmt.PARAMETERS:
self.t.env.params})
try:
update_task = update.StackUpdate(
self, newstack, backup_stack,
@ -1194,6 +1197,12 @@ class Stack(collections.Mapping):
notification.send(self)
self._add_event(self.action, self.status, self.status_reason)
if self.status == self.FAILED:
# Since template was incrementally updated based on existing and
# new stack resources, we should have user params of both.
existing_params.load(newstack.t.env.user_env_as_dict())
self.t.env = existing_params
self.t.store(self.context)
self.store()
lifecycle_plugin_utils.do_post_ops(self.context, self,

View File

@ -19,6 +19,7 @@ from heat.common import template_format
from heat.engine import environment
from heat.engine import resource
from heat.engine import scheduler
from heat.engine import service
from heat.engine import stack
from heat.engine import template
from heat.tests import common
@ -1385,6 +1386,107 @@ class StackUpdateTest(common.HeatTestCase):
mock_delete_A.assert_called_once_with()
self.assertEqual(2, mock_create.call_count)
def test_update_failure_recovery_new_param_stack_list(self):
'''
assertion:
check that stack-list is not broken if update fails in between.
Also ensure that next update passes
'''
class ResourceTypeA(generic_rsrc.ResourceWithProps):
count = 0
def handle_create(self):
ResourceTypeA.count += 1
self.resource_id_set('%s%d' % (self.name, self.count))
def handle_delete(self):
return super(ResourceTypeA, self).handle_delete()
resource._register_class('ResourceTypeA', ResourceTypeA)
tmpl = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'abc-param': {'Type': 'String'}
},
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': {'Ref': 'abc-param'}}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AResource'}}}
}
}
env1 = environment.Environment({'abc-param': 'abc'})
tmpl2 = {
'HeatTemplateFormatVersion': '2012-12-12',
'Parameters': {
'smelly-param': {'Type': 'String'}
},
'Resources': {
'AResource': {'Type': 'ResourceTypeA',
'Properties': {'Foo': {'Ref': 'smelly-param'}}},
'BResource': {'Type': 'ResourceWithPropsType',
'Properties': {'Foo': {'Ref': 'AResource'}}}
}
}
env2 = environment.Environment({'smelly-param': 'smelly'})
self.stack = stack.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl, env=env1),
disable_rollback=True)
self.stack.store()
self.stack.create()
self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE),
self.stack.state)
self.assertEqual('abc', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource1',
self.stack['BResource'].properties['Foo'])
mock_create = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_create',
side_effect=[Exception, None])
mock_delete = self.patchobject(generic_rsrc.ResourceWithProps,
'handle_delete')
mock_delete_A = self.patchobject(ResourceTypeA, 'handle_delete')
updated_stack = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2, env=env2),
disable_rollback=True)
self.stack.update(updated_stack)
# Ensure UPDATE FAILED
mock_create.assert_called_once_with()
self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED),
self.stack.state)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
# check if heat stack-list works, wherein it tries to fetch template
# parameters value from env
self.eng = service.EngineService('a-host', 'a-topic')
self.eng.list_stacks(self.ctx)
# Check if next update works fine
self.stack = stack.Stack.load(self.ctx, self.stack.id)
updated_stack2 = stack.Stack(self.ctx, 'updated_stack',
template.Template(tmpl2, env=env2),
disable_rollback=True)
self.stack.update(updated_stack2)
self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE),
self.stack.state)
self.stack = stack.Stack.load(self.ctx, self.stack.id)
self.assertEqual('smelly', self.stack['AResource'].properties['Foo'])
self.assertEqual('AResource2',
self.stack['BResource'].properties['Foo'])
self.assertEqual(2, mock_delete.call_count)
mock_delete_A.assert_called_once_with()
self.assertEqual(2, mock_create.call_count)
def test_update_replace_parameters(self):
'''
assertion: