diff options
author | Thomas Herve <therve@redhat.com> | 2015-11-09 09:32:00 +0100 |
---|---|---|
committer | Thomas Herve <therve@redhat.com> | 2015-11-18 16:40:16 +0100 |
commit | c0457f17fca7004014f47f6f84ce286d3df0e0c1 (patch) | |
tree | 9ee43038039251ae780f701635cc0873d10f278f | |
parent | f9e155dd6f7ef0a04ffd2ab426badc3720a13699 (diff) | |
download | heat-c0457f17fca7004014f47f6f84ce286d3df0e0c1.tar.gz |
Implement pre-delete hook
Add a hook running before resource deletion.
blueprint pre-delete-hook
Depends-On: Ia362650261b0b6c03ce431cbcf292a1685b59883
Change-Id: I8f57cab5ea9ff54d84195add1ba5065520e83489
-rw-r--r-- | heat/engine/environment.py | 6 | ||||
-rw-r--r-- | heat/engine/resource.py | 7 | ||||
-rw-r--r-- | heat/tests/test_environment.py | 27 | ||||
-rw-r--r-- | heat/tests/test_resource.py | 24 | ||||
-rw-r--r-- | heat/tests/test_stack_update.py | 37 |
5 files changed, 94 insertions, 7 deletions
diff --git a/heat/engine/environment.py b/heat/engine/environment.py index ada4a16a1..e99529dda 100644 --- a/heat/engine/environment.py +++ b/heat/engine/environment.py @@ -35,7 +35,11 @@ from heat.engine import support LOG = log.getLogger(__name__) -HOOK_TYPES = (HOOK_PRE_CREATE, HOOK_PRE_UPDATE) = ('pre-create', 'pre-update') +HOOK_TYPES = ( + HOOK_PRE_CREATE, HOOK_PRE_UPDATE, HOOK_PRE_DELETE +) = ( + 'pre-create', 'pre-update', 'pre-delete' +) def valid_hook_type(hook): diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 9f9dc26be..1b9bf5613 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -1250,6 +1250,13 @@ class Resource(object): initial_state = self.state + # This method can be called when we replace a resource, too. In that + # case, a hook has already been dealt with in `Resource.update` so we + # shouldn't do it here again: + if self.stack.action == self.stack.DELETE: + yield self._break_if_required( + self.DELETE, environment.HOOK_PRE_DELETE) + LOG.info(_LI('deleting %s'), six.text_type(self)) if self._stored_properties_data is not None: diff --git a/heat/tests/test_environment.py b/heat/tests/test_environment.py index fab3aab4b..24c8b5a1a 100644 --- a/heat/tests/test_environment.py +++ b/heat/tests/test_environment.py @@ -735,7 +735,8 @@ class ResourceRegistryTest(common.HeatTestCase): registry = environment.ResourceRegistry(None, {}) msg = ('Invalid hook type "invalid-type" for resource breakpoint, ' - 'acceptable hook types are: (\'pre-create\', \'pre-update\')') + 'acceptable hook types are: (\'pre-create\', \'pre-update\', ' + '\'pre-delete\')') ex = self.assertRaises(exception.InvalidBreakPointHook, registry.load, {'resources': resources}) self.assertEqual(msg, six.text_type(ex)) @@ -880,8 +881,11 @@ class HookMatchTest(common.HeatTestCase): u'pre_update': { u'hooks': 'pre-update', }, - u'both': { - u'hooks': ['pre-create', 'pre-update'], + u'pre_delete': { + u'hooks': 'pre-delete', + }, + u'all': { + u'hooks': ['pre-create', 'pre-update', 'pre-delete'], }, } registry = environment.ResourceRegistry(None, {}) @@ -891,13 +895,26 @@ class HookMatchTest(common.HeatTestCase): 'pre_create', environment.HOOK_PRE_CREATE)) self.assertFalse(registry.matches_hook( 'pre_create', environment.HOOK_PRE_UPDATE)) + self.assertFalse(registry.matches_hook( + 'pre_create', environment.HOOK_PRE_DELETE)) self.assertTrue(registry.matches_hook( 'pre_update', environment.HOOK_PRE_UPDATE)) self.assertFalse(registry.matches_hook( 'pre_update', environment.HOOK_PRE_CREATE)) + self.assertFalse(registry.matches_hook( + 'pre_update', environment.HOOK_PRE_DELETE)) self.assertTrue(registry.matches_hook( - 'both', environment.HOOK_PRE_CREATE)) + 'pre_delete', environment.HOOK_PRE_DELETE)) + self.assertFalse(registry.matches_hook( + 'pre_delete', environment.HOOK_PRE_CREATE)) + self.assertFalse(registry.matches_hook( + 'pre_delete', environment.HOOK_PRE_UPDATE)) + + self.assertTrue(registry.matches_hook( + 'all', environment.HOOK_PRE_CREATE)) + self.assertTrue(registry.matches_hook( + 'all', environment.HOOK_PRE_UPDATE)) self.assertTrue(registry.matches_hook( - 'both', environment.HOOK_PRE_UPDATE)) + 'all', environment.HOOK_PRE_DELETE)) diff --git a/heat/tests/test_resource.py b/heat/tests/test_resource.py index a2892414f..bef3b26ce 100644 --- a/heat/tests/test_resource.py +++ b/heat/tests/test_resource.py @@ -2810,6 +2810,11 @@ class ResourceHookTest(common.HeatTestCase): self.assertFalse(res.has_hook('pre-create')) self.assertTrue(res.has_hook('pre-update')) + res.data = mock.Mock(return_value={'pre-delete': 'True'}) + self.assertFalse(res.has_hook('pre-create')) + self.assertFalse(res.has_hook('pre-update')) + self.assertTrue(res.has_hook('pre-delete')) + def test_set_hook(self): snippet = rsrc_defn.ResourceDefinition('res', 'GenericResourceType') @@ -2856,7 +2861,7 @@ class ResourceHookTest(common.HeatTestCase): self.assertRaises(exception.InvalidBreakPointHook, res.signal, {'unset_hook': 'pre-create'}) - def test_hook_call(self): + def test_pre_create_hook_call(self): self.stack.env.registry.load( {'resources': {'res': {'hooks': 'pre-create'}}}) snippet = rsrc_defn.ResourceDefinition('res', @@ -2871,6 +2876,23 @@ class ResourceHookTest(common.HeatTestCase): task.run_to_completion() self.assertEqual((res.CREATE, res.COMPLETE), res.state) + def test_pre_delete_hook_call(self): + self.stack.env.registry.load( + {'resources': {'res': {'hooks': 'pre-delete'}}}) + snippet = rsrc_defn.ResourceDefinition('res', + 'GenericResourceType') + res = resource.Resource('res', snippet, self.stack) + res.id = '1234' + res.action = 'CREATE' + self.stack.action = 'DELETE' + task = scheduler.TaskRunner(res.delete) + task.start() + task.step() + self.assertTrue(res.has_hook('pre-delete')) + res.clear_hook('pre-delete') + task.run_to_completion() + self.assertEqual((res.DELETE, res.COMPLETE), res.state) + class ResourceAvailabilityTest(common.HeatTestCase): def _mock_client_plugin(self, service_types=[], is_available=True): diff --git a/heat/tests/test_stack_update.py b/heat/tests/test_stack_update.py index 4499212aa..f137107d8 100644 --- a/heat/tests/test_stack_update.py +++ b/heat/tests/test_stack_update.py @@ -404,6 +404,43 @@ class StackUpdateTest(common.HeatTestCase): self.stack.state) self.assertEqual('xyz', self.stack['AResource'].properties['Foo']) + def test_update_replace_delete_hook(self): + tmpl = { + 'HeatTemplateFormatVersion': '2012-12-12', + 'Parameters': { + 'foo': {'Type': 'String'} + }, + 'Resources': { + 'AResource': { + 'Type': 'ResourceWithPropsType', + 'Properties': {'Foo': {'Ref': 'foo'}} + } + } + } + + env = environment.Environment({'foo': 'abc'}) + env.registry.load( + {'resources': {'AResource': {'hooks': 'pre-delete'}}}) + self.stack = stack.Stack( + self.ctx, 'update_test_stack', + template.Template(tmpl, env=env)) + self.stack.store() + self.stack.create() + self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE), + self.stack.state) + + env2 = environment.Environment({'foo': 'xyz'}) + env2.registry.load( + {'resources': {'AResource': {'hooks': 'pre-delete'}}}) + updated_stack = stack.Stack(self.ctx, 'updated_stack', + template.Template(tmpl, env=env2)) + + self.stack.update(updated_stack) + # The hook is not called, and update succeeds properly + self.assertEqual((stack.Stack.UPDATE, stack.Stack.COMPLETE), + self.stack.state) + self.assertEqual('xyz', self.stack['AResource'].properties['Foo']) + def test_update_modify_update_failed(self): tmpl = {'HeatTemplateFormatVersion': '2012-12-12', 'Resources': {'AResource': {'Type': 'ResourceWithPropsType', |