diff --git a/heatclient/common/event_utils.py b/heatclient/common/event_utils.py index df720d83..65f89c55 100644 --- a/heatclient/common/event_utils.py +++ b/heatclient/common/event_utils.py @@ -31,6 +31,10 @@ def get_hook_events(hc, stack_id, event_args, nested_depth=0, stack_action_reason = 'Stack UPDATE started' hook_event_reason = 'UPDATE paused until Hook pre-update is cleared' hook_clear_event_reason = 'Hook pre-update is cleared' + elif hook_type == 'pre-delete': + stack_action_reason = 'Stack DELETE started' + hook_event_reason = 'DELETE paused until Hook pre-delete is cleared' + hook_clear_event_reason = 'Hook pre-delete is cleared' else: raise exc.CommandError(_('Unexpected hook type %s') % hook_type) diff --git a/heatclient/common/hook_utils.py b/heatclient/common/hook_utils.py index 5cef4342..c877f238 100644 --- a/heatclient/common/hook_utils.py +++ b/heatclient/common/hook_utils.py @@ -56,8 +56,9 @@ def clear_wildcard_hooks(hc, stack_id, stack_patterns, hook_type, def get_hook_type_via_status(hc, stack_id): - # Figure out if the hook should be pre-create or pre-update based - # on the stack status, also sanity assertions that we're in-progress. + # Figure out if the hook should be pre-create, pre-update or + # pre-delete based on the stack status, also sanity assertions + # that we're in-progress. try: stack = hc.stacks.get(stack_id=stack_id) except exc.HTTPNotFound: @@ -71,8 +72,10 @@ def get_hook_type_via_status(hc, stack_id): hook_type = 'pre-create' elif 'UPDATE' in stack.stack_status: hook_type = 'pre-update' + elif 'DELETE' in stack.stack_status: + hook_type = 'pre-delete' else: raise exc.CommandError(_('Unexpected stack status %s, ' - 'only create/update supported') + 'only create, update and delete supported') % stack.stack_status) return hook_type diff --git a/heatclient/osc/v1/stack.py b/heatclient/osc/v1/stack.py index ead9c6f9..26781b70 100644 --- a/heatclient/osc/v1/stack.py +++ b/heatclient/osc/v1/stack.py @@ -1219,6 +1219,11 @@ class StackHookClear(command.Command): action='store_true', help=_('Clear the pre-update hooks') ) + parser.add_argument( + '--pre-delete', + action='store_true', + help=_('Clear the pre-delete hooks') + ) parser.add_argument( 'hook', metavar='', @@ -1246,6 +1251,8 @@ def _hook_clear(args, heat_client): hook_type = 'pre-create' elif args.pre_update: hook_type = 'pre-update' + elif args.pre_delete: + hook_type = 'pre-delete' else: hook_type = hook_utils.get_hook_type_via_status(heat_client, args.stack) diff --git a/heatclient/tests/unit/osc/v1/test_stack.py b/heatclient/tests/unit/osc/v1/test_stack.py index 0755cf15..3104cd8d 100644 --- a/heatclient/tests/unit/osc/v1/test_stack.py +++ b/heatclient/tests/unit/osc/v1/test_stack.py @@ -1299,3 +1299,12 @@ class TestStackHookClear(TestStack): data={'unset_hook': 'pre-update'}, resource_name='resource', stack_id='my_stack') + + def test_hook_clear_pre_delete(self): + arglist = ['my_stack', 'resource', '--pre-delete'] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.cmd.take_action(parsed_args) + self.mock_client.resources.signal.assert_called_once_with( + data={'unset_hook': 'pre-delete'}, + resource_name='resource', + stack_id='my_stack') diff --git a/heatclient/tests/unit/test_shell.py b/heatclient/tests/unit/test_shell.py index dd91e0eb..062dfd4a 100644 --- a/heatclient/tests/unit/test_shell.py +++ b/heatclient/tests/unit/test_shell.py @@ -3718,6 +3718,21 @@ class ShellTestHookFunctions(ShellBase): self.assertNotRegexpMatches(list_text, 'n_eventid1') self.assertNotRegexpMatches(list_text, 'n_eventid2') + def test_hook_poll_pre_delete(self): + self.register_keystone_auth_fixture() + stack_id = 'teststack/1' + nested_id = 'nested/2' + self._stub_responses(stack_id, nested_id, 'DELETE') + self.m.ReplayAll() + list_text = self.shell('hook-poll %s --nested-depth 1' % stack_id) + hook_reason = 'DELETE paused until Hook pre-delete is cleared' + required = ['id', 'p_eventid2', 'stack_name', 'teststack', hook_reason] + for r in required: + self.assertRegex(list_text, r) + self.assertNotRegexpMatches(list_text, 'p_eventid1') + self.assertNotRegexpMatches(list_text, 'n_eventid1') + self.assertNotRegexpMatches(list_text, 'n_eventid2') + def test_hook_poll_bad_status(self): self.register_keystone_auth_fixture() stack_id = 'teststack/1' @@ -3752,12 +3767,12 @@ class ShellTestHookFunctions(ShellBase): def test_hook_poll_clear_bad_action(self): self.register_keystone_auth_fixture() stack_id = 'teststack/1' - self._stub_stack_response(stack_id, action='DELETE') + self._stub_stack_response(stack_id, action='BADACTION') self.m.ReplayAll() error = self.assertRaises( exc.CommandError, self.shell, 'hook-clear %s aresource' % stack_id) - self.assertIn('Unexpected stack status DELETE_IN_PROGRESS', + self.assertIn('Unexpected stack status BADACTION_IN_PROGRESS', str(error)) diff --git a/heatclient/tests/unit/v1/test_hooks.py b/heatclient/tests/unit/v1/test_hooks.py index d2fd947b..70a190b7 100644 --- a/heatclient/tests/unit/v1/test_hooks.py +++ b/heatclient/tests/unit/v1/test_hooks.py @@ -269,6 +269,21 @@ class TestHooks(testtools.TestCase): self.assertEqual('bp', payload['resource_name']) self.assertEqual('mystack', payload['stack_id']) + def test_clear_pre_delete_hooks(self): + type(self.args).hook = mock.PropertyMock( + return_value=['bp']) + type(self.args).pre_delete = mock.PropertyMock(return_value=True) + bp = mock.Mock() + type(bp).resource_name = 'bp' + self.client.resources.list = mock.Mock(return_value=[bp]) + + shell.do_hook_clear(self.client, self.args) + self.assertEqual(1, self.client.resources.signal.call_count) + payload = self.client.resources.signal.call_args_list[0][1] + self.assertEqual({'unset_hook': 'pre-delete'}, payload['data']) + self.assertEqual('bp', payload['resource_name']) + self.assertEqual('mystack', payload['stack_id']) + def test_clear_nested_hook(self): type(self.args).hook = mock.PropertyMock( return_value=['a/b/bp']) diff --git a/heatclient/v1/shell.py b/heatclient/v1/shell.py index 05a5d293..9d5ff60b 100644 --- a/heatclient/v1/shell.py +++ b/heatclient/v1/shell.py @@ -1104,6 +1104,8 @@ def do_resource_mark_unhealthy(hc, args): help=_('Clear the pre-create hooks (optional)')) @utils.arg('--pre-update', action='store_true', default=False, help=_('Clear the pre-update hooks (optional)')) +@utils.arg('--pre-delete', action='store_true', default=False, + help=_('Clear the pre-delete hooks (optional)')) @utils.arg('hook', metavar='', nargs='+', help=_('Resource names with hooks to clear. Resources ' 'in nested stacks can be set using slash as a separator: ' @@ -1118,6 +1120,8 @@ def do_hook_clear(hc, args): hook_type = 'pre-create' elif args.pre_update: hook_type = 'pre-update' + elif args.pre_delete: + hook_type = 'pre-delete' else: hook_type = hook_utils.get_hook_type_via_status(hc, args.id)