Move rollback logic to stack class

Currently stack rollback logic is in the worker class which
makes stack rollback code to be duplicated to other places
like the simulater tests.

Change-Id: I2c907ca1c76b39addc22a5d8c36fb50c47af4785
This commit is contained in:
tyagi 2015-07-20 04:49:23 -07:00
parent bd7895c555
commit 90e2a26539
4 changed files with 68 additions and 58 deletions

View File

@ -1005,6 +1005,18 @@ class Stack(collections.Mapping):
self.current_traversal,
input_data, is_update)
def rollback(self):
old_tmpl_id = self.prev_raw_template_id
if old_tmpl_id is None:
rollback_tmpl = tmpl.Template.create_empty_template(
version=self.t.version)
else:
rollback_tmpl = tmpl.Template.load(self.context, old_tmpl_id)
self.prev_raw_template_id = None
self.store()
self.converge_stack(rollback_tmpl, action=self.ROLLBACK)
def _get_best_existing_rsrc_db(self, rsrc_name):
candidate = None
if self.ext_rsrcs_db:

View File

@ -28,7 +28,6 @@ from heat.engine import dependencies
from heat.engine import resource
from heat.engine import stack as parser
from heat.engine import sync_point
from heat.engine import template as templatem
from heat.objects import resource as resource_objects
from heat.rpc import listener_client
from heat.rpc import worker_client as rpc_client
@ -99,19 +98,10 @@ class WorkerService(service.Service):
return True
return False
def _trigger_rollback(self, cnxt, stack):
def _trigger_rollback(self, stack):
LOG.info(_LI("Triggering rollback of %(stack_name)s %(action)s "),
{'action': stack.action, 'stack_name': stack.name})
old_tmpl_id = stack.prev_raw_template_id
if old_tmpl_id is None:
rollback_tmpl = templatem.Template.create_empty_template(
version=stack.t.version)
else:
rollback_tmpl = templatem.Template.load(cnxt, old_tmpl_id)
stack.prev_raw_template_id = None
stack.store()
stack.converge_stack(rollback_tmpl, action=stack.ROLLBACK)
stack.rollback()
def _handle_resource_failure(self, cnxt, stack_id, traversal_id,
failure_reason):
@ -124,7 +114,7 @@ class WorkerService(service.Service):
if (not stack.disable_rollback and
stack.action in (stack.CREATE, stack.ADOPT, stack.UPDATE)):
self._trigger_rollback(cnxt, stack)
self._trigger_rollback(stack)
else:
stack.purge_db()

View File

@ -16,11 +16,9 @@
import mock
from heat.common import exception
from heat.common import template_format
from heat.engine import resource
from heat.engine import stack
from heat.engine import sync_point
from heat.engine import template as templatem
from heat.engine import worker
from heat.rpc import worker_client
from heat.tests import common
@ -273,7 +271,7 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
self.assertTrue(self.worker._trigger_rollback.called)
# make sure the rollback is called on given stack
call_args, call_kwargs = self.worker._trigger_rollback.call_args
called_stack = call_args[1]
called_stack = call_args[0]
self.assertEqual(self.stack.id, called_stack.id)
def test_resource_cleanup_failure_triggers_rollback_if_enabled(
@ -292,7 +290,7 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
self.assertTrue(self.worker._trigger_rollback.called)
# make sure the rollback is called on given stack
call_args, call_kwargs = self.worker._trigger_rollback.call_args
called_stack = call_args[1]
called_stack = call_args[0]
self.assertEqual(self.stack.id, called_stack.id)
def test_rollback_is_not_triggered_on_rollback_disabled_stack(
@ -325,47 +323,6 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
self.is_update)
self.assertFalse(self.worker._trigger_rollback.called)
def test_trigger_rollback_uses_old_template_if_available(
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
# create a template and assign to stack as previous template
t = template_format.parse(tools.wp_template)
prev_tmpl = templatem.Template(t)
prev_tmpl.store(context=self.ctx)
self.stack.prev_raw_template_id = prev_tmpl.id
# mock failure
self.stack.action = self.stack.UPDATE
self.stack.status = self.stack.FAILED
self.stack.store()
# mock converge_stack()
self.stack.converge_stack = mock.Mock()
# call trigger_rollbac
self.worker._trigger_rollback(self.ctx, self.stack)
# Make sure stack converge is called with previous template
self.assertTrue(self.stack.converge_stack.called)
self.assertIsNone(self.stack.prev_raw_template_id)
call_args, call_kwargs = self.stack.converge_stack.call_args
template_used_for_rollback = call_args[0]
self.assertEqual(prev_tmpl.id, template_used_for_rollback.id)
def test_trigger_rollback_uses_empty_template_if_prev_tmpl_not_available(
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
# mock create failure with no previous template
self.stack.prev_raw_template_id = None
self.stack.action = self.stack.CREATE
self.stack.status = self.stack.FAILED
self.stack.store()
# mock converge_stack()
self.stack.converge_stack = mock.Mock()
# call trigger_rollback
self.worker._trigger_rollback(self.ctx, self.stack)
# Make sure stack converge is called with empty template
self.assertTrue(self.stack.converge_stack.called)
call_args, call_kwargs = self.stack.converge_stack.call_args
template_used_for_rollback = call_args[0]
self.assertEqual({}, template_used_for_rollback['resources'])
def test_resource_update_failure_purges_db_for_stack_failure(
self, mock_cru, mock_crc, mock_pcr, mock_csc, mock_cid):
self.stack.disable_rollback = True

View File

@ -37,6 +37,7 @@ from heat.objects import stack as stack_object
from heat.objects import stack_tag as stack_tag_object
from heat.objects import user_creds as ucreds_object
from heat.tests import common
from heat.tests.engine import tools
from heat.tests import fakes
from heat.tests import generic_resource as generic_rsrc
from heat.tests import utils
@ -2208,3 +2209,53 @@ class StackKwargsForCloningTest(common.HeatTestCase):
# just make sure that the kwargs are valid
# (no exception should be raised)
stack.Stack(ctx, utils.random_name(), tmpl, **res)
class TestStackRollback(common.HeatTestCase):
def setUp(self):
super(TestStackRollback, self).setUp()
self.ctx = utils.dummy_context()
self.stack = tools.get_stack('test_stack_rollback', self.ctx,
template=tools.string_template_five,
convergence=True)
def test_trigger_rollback_uses_old_template_if_available(self):
# create a template and assign to stack as previous template
t = template_format.parse(tools.wp_template)
prev_tmpl = template.Template(t)
prev_tmpl.store(context=self.ctx)
self.stack.prev_raw_template_id = prev_tmpl.id
# mock failure
self.stack.action = self.stack.UPDATE
self.stack.status = self.stack.FAILED
self.stack.store()
# mock converge_stack()
self.stack.converge_stack = mock.Mock()
# call trigger_rollbac
self.stack.rollback()
# Make sure stack converge is called with previous template
self.assertTrue(self.stack.converge_stack.called)
self.assertIsNone(self.stack.prev_raw_template_id)
call_args, call_kwargs = self.stack.converge_stack.call_args
template_used_for_rollback = call_args[0]
self.assertEqual(prev_tmpl.id, template_used_for_rollback.id)
def test_trigger_rollback_uses_empty_template_if_prev_tmpl_not_available(
self):
# mock create failure with no previous template
self.stack.prev_raw_template_id = None
self.stack.action = self.stack.CREATE
self.stack.status = self.stack.FAILED
self.stack.store()
# mock converge_stack()
self.stack.converge_stack = mock.Mock()
# call trigger_rollback
self.stack.rollback()
# Make sure stack converge is called with empty template
self.assertTrue(self.stack.converge_stack.called)
call_args, call_kwargs = self.stack.converge_stack.call_args
template_used_for_rollback = call_args[0]
self.assertEqual({}, template_used_for_rollback['resources'])