summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Herve <therve@redhat.com>2015-11-09 09:32:00 +0100
committerThomas Herve <therve@redhat.com>2015-11-18 16:40:16 +0100
commitc0457f17fca7004014f47f6f84ce286d3df0e0c1 (patch)
tree9ee43038039251ae780f701635cc0873d10f278f
parentf9e155dd6f7ef0a04ffd2ab426badc3720a13699 (diff)
downloadheat-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.py6
-rw-r--r--heat/engine/resource.py7
-rw-r--r--heat/tests/test_environment.py27
-rw-r--r--heat/tests/test_resource.py24
-rw-r--r--heat/tests/test_stack_update.py37
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',