Cancel traversal of nested stack
The stack cancel update would halt the parent stack from propagating but
the nested stacks kept on going till they either failed or completed.
This is not desired, the cancel update should stop all the nested stacks
from moving further, albeit, it shouldn't abruptly stop the currently
running workers.
Change-Id: I3e1c58bbe4f92e2d2bfea539f3d0e861a3a7cef1
Co-Authored-By: Zane Bitter <zbitter@redhat.com>
Closes-Bug: #1623201
(cherry picked from commit bc2e136fe3
)
This commit is contained in:
parent
c7fe0ba5ee
commit
25fbedc726
|
@ -28,6 +28,7 @@ from heat.common.i18n import _LW
|
|||
from heat.common import messaging as rpc_messaging
|
||||
from heat.db import api as db_api
|
||||
from heat.engine import check_resource
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import sync_point
|
||||
from heat.objects import stack as stack_objects
|
||||
from heat.rpc import api as rpc_api
|
||||
|
@ -104,24 +105,17 @@ class WorkerService(service.Service):
|
|||
in_progress resources to complete normally; no worker is stopped
|
||||
abruptly.
|
||||
"""
|
||||
old_trvsl = stack.current_traversal
|
||||
updated = _update_current_traversal(stack)
|
||||
if not updated:
|
||||
LOG.warning(_LW("Failed to update stack %(name)s with new "
|
||||
"traversal, aborting stack cancel"),
|
||||
{'name': stack.name})
|
||||
return
|
||||
_stop_traversal(stack)
|
||||
|
||||
reason = 'User cancelled stack %s ' % stack.action
|
||||
updated = stack.state_set(stack.action, stack.FAILED, reason)
|
||||
if not updated:
|
||||
LOG.warning(_LW("Failed to update stack %(name)s status"
|
||||
" to %(action)_%(state)"),
|
||||
{'name': stack.name, 'action': stack.action,
|
||||
'state': stack.FAILED})
|
||||
return
|
||||
db_child_stacks = stack_objects.Stack.get_all_by_root_owner_id(
|
||||
stack.context, stack.id)
|
||||
|
||||
sync_point.delete_all(stack.context, stack.id, old_trvsl)
|
||||
for db_child in db_child_stacks:
|
||||
if db_child.status == parser.Stack.IN_PROGRESS:
|
||||
child = parser.Stack.load(stack.context,
|
||||
stack_id=db_child.id,
|
||||
stack=db_child)
|
||||
_stop_traversal(child)
|
||||
|
||||
def stop_all_workers(self, stack):
|
||||
# stop the traversal
|
||||
|
@ -184,6 +178,27 @@ class WorkerService(service.Service):
|
|||
_cancel_check_resource(stack_id, self.engine_id, self.thread_group_mgr)
|
||||
|
||||
|
||||
def _stop_traversal(stack):
|
||||
old_trvsl = stack.current_traversal
|
||||
updated = _update_current_traversal(stack)
|
||||
if not updated:
|
||||
LOG.warning(_LW("Failed to update stack %(name)s with new "
|
||||
"traversal, aborting stack cancel"),
|
||||
{'name': stack.name})
|
||||
return
|
||||
|
||||
reason = 'Stack %(action)s cancelled' % {'action': stack.action}
|
||||
updated = stack.state_set(stack.action, stack.FAILED, reason)
|
||||
if not updated:
|
||||
LOG.warning(_LW("Failed to update stack %(name)s status"
|
||||
" to %(action)_%(state)"),
|
||||
{'name': stack.name, 'action': stack.action,
|
||||
'state': stack.FAILED})
|
||||
return
|
||||
|
||||
sync_point.delete_all(stack.context, stack.id, old_trvsl)
|
||||
|
||||
|
||||
def _update_current_traversal(stack):
|
||||
previous_traversal = stack.current_traversal
|
||||
stack.current_traversal = uuidutils.generate_uuid()
|
||||
|
|
|
@ -17,6 +17,8 @@ import mock
|
|||
|
||||
from heat.db import api as db_api
|
||||
from heat.engine import check_resource
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import template as templatem
|
||||
from heat.engine import worker
|
||||
from heat.objects import stack as stack_objects
|
||||
from heat.rpc import worker_client as wc
|
||||
|
@ -178,6 +180,26 @@ class WorkerServiceTest(common.HeatTestCase):
|
|||
mock_ccr.assert_has_calls(calls, any_order=True)
|
||||
self.assertTrue(mock_wc.called)
|
||||
|
||||
@mock.patch.object(worker, '_stop_traversal')
|
||||
def test_stop_traversal_stops_nested_stack(self, mock_st):
|
||||
mock_tgm = mock.Mock()
|
||||
ctx = utils.dummy_context()
|
||||
tmpl = templatem.Template.create_empty_template()
|
||||
stack1 = parser.Stack(ctx, 'stack1', tmpl,
|
||||
current_traversal='123')
|
||||
stack1.store()
|
||||
stack2 = parser.Stack(ctx, 'stack2', tmpl,
|
||||
owner_id=stack1.id, current_traversal='456')
|
||||
stack2.store()
|
||||
_worker = worker.WorkerService('host-1', 'topic-1', 'engine-001',
|
||||
mock_tgm)
|
||||
_worker.stop_traversal(stack1)
|
||||
self.assertEqual(2, mock_st.call_count)
|
||||
call1, call2 = mock_st.call_args_list
|
||||
call_args1, call_args2 = call1[0][0], call2[0][0]
|
||||
self.assertEqual('stack1', call_args1.name)
|
||||
self.assertEqual('stack2', call_args2.name)
|
||||
|
||||
@mock.patch.object(worker, '_cancel_workers')
|
||||
@mock.patch.object(worker.WorkerService, 'stop_traversal')
|
||||
def test_stop_all_workers_when_stack_in_progress(self, mock_st, mock_cw):
|
||||
|
|
Loading…
Reference in New Issue