From 02b6d5f984aac5a917f995e35f96eabc5e079fb7 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Thu, 19 Jun 2014 14:41:09 +0100 Subject: [PATCH] Convert WaitConditionHandle to use handle_signal The metadata_update interface does pretty much the exact same thing as handle_signal, with the disadvantage that it's not exposed via the native ReST API, so convert WaitConditionHandle to use handle_signal. This has the following advantages: - Provides a step towards native waitcondition resources - Enables signalling existing waitcondition resources via heat resource-signal (e.g the native API) - Moves towards a standardized model where handle_signal is the only supported plugin interface for asynchronous signalling of resources. To avoid breaking any CD folks who may not update heat-engine and heat-api-cfn at the same time, the existing resource interface is maintained with a deprecation warning (how long to maintain this is TBC). Change-Id: Id7dde1c5f13683950a1cb483ae2ce4d1fbf2a56e blueprint: native-waitcondition --- heat/engine/resources/wait_condition.py | 16 +++--- heat/engine/service.py | 8 +++ heat/tests/test_metadata_refresh.py | 5 +- heat/tests/test_waitcondition.py | 66 ++++++++++++------------- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/heat/engine/resources/wait_condition.py b/heat/engine/resources/wait_condition.py index 67b3e357f9..e675850e3f 100644 --- a/heat/engine/resources/wait_condition.py +++ b/heat/engine/resources/wait_condition.py @@ -66,21 +66,25 @@ class WaitConditionHandle(signal_responder.SignalResponder): return metadata['Status'] in WAIT_STATUSES def metadata_update(self, new_metadata=None): + """DEPRECATED. Should use handle_signal instead.""" + self.handle_signal(details=new_metadata) + + def handle_signal(self, details=None): ''' Validate and update the resource metadata ''' - if new_metadata is None: + if details is None: return - if self._metadata_format_ok(new_metadata): + if self._metadata_format_ok(details): rsrc_metadata = self.metadata_get(refresh=True) - if new_metadata['UniqueId'] in rsrc_metadata: + if details['UniqueId'] in rsrc_metadata: LOG.warning(_("Overwriting Metadata item for UniqueId %s!") - % new_metadata['UniqueId']) + % details['UniqueId']) safe_metadata = {} for k in ('Data', 'Reason', 'Status'): - safe_metadata[k] = new_metadata[k] - rsrc_metadata.update({new_metadata['UniqueId']: safe_metadata}) + safe_metadata[k] = details[k] + rsrc_metadata.update({details['UniqueId']: safe_metadata}) self.metadata_set(rsrc_metadata) else: LOG.error(_("Metadata failed validation for %s") % self.name) diff --git a/heat/engine/service.py b/heat/engine/service.py index 0ba59f59be..53ca88f379 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -937,6 +937,14 @@ class EngineService(service.Service): if callable(stack[resource_name].signal): stack[resource_name].signal(details) + # Refresh the metadata for all other resources, since signals can + # update metadata which is used by other resources, e.g + # when signalling a WaitConditionHandle resource, and other + # resources may refer to WaitCondition Fn::GetAtt Data + for res in stack.dependencies: + if res.name != resource_name and res.id is not None: + res.metadata_update() + @request_context def find_physical_resource(self, cnxt, physical_resource_id): """ diff --git a/heat/tests/test_metadata_refresh.py b/heat/tests/test_metadata_refresh.py index f05875dcf7..3a5b9ce44c 100644 --- a/heat/tests/test_metadata_refresh.py +++ b/heat/tests/test_metadata_refresh.py @@ -259,11 +259,10 @@ class WaitCondMetadataUpdateTest(HeatTestCase): ''' 1 create stack 2 assert empty instance metadata - 3 service.metadata_update() + 3 service.resource_signal() 4 assert valid waitcond metadata 5 assert valid instance metadata ''' - self.stack = self.create_stack() watch = self.stack['WC'] @@ -274,7 +273,7 @@ class WaitCondMetadataUpdateTest(HeatTestCase): self.assertIsNone(inst.metadata_get()['test']) def update_metadata(id, data, reason): - self.man.metadata_update(utils.dummy_context(), + self.man.resource_signal(utils.dummy_context(), dict(self.stack.identifier()), 'WH', {'Data': data, 'Reason': reason, diff --git a/heat/tests/test_waitcondition.py b/heat/tests/test_waitcondition.py index 7e5fcd0d41..19be43b162 100644 --- a/heat/tests/test_waitcondition.py +++ b/heat/tests/test_waitcondition.py @@ -262,13 +262,13 @@ class WaitConditionTest(HeatTestCase): test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} - handle.metadata_update(new_metadata=test_metadata) + handle.handle_signal(test_metadata) wc_att = rsrc.FnGetAtt('Data') self.assertEqual('{"123": "foo"}', wc_att) test_metadata = {'Data': 'dog', 'Reason': 'cat', 'Status': 'SUCCESS', 'UniqueId': '456'} - handle.metadata_update(new_metadata=test_metadata) + handle.handle_signal(test_metadata) wc_att = rsrc.FnGetAtt('Data') self.assertEqual(u'{"123": "foo", "456": "dog"}', wc_att) self.m.VerifyAll() @@ -431,61 +431,61 @@ class WaitConditionHandleTest(HeatTestCase): self.assertEqual(unicode(expected_url), rsrc.FnGetRefId()) self.m.VerifyAll() - def test_metadata_update(self): + def test_handle_signal(self): self.stack = self.create_stack() rsrc = self.stack['WaitHandle'] self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) handle_metadata = {u'123': {u'Data': u'foo', u'Reason': u'bar', u'Status': u'SUCCESS'}} self.assertEqual(handle_metadata, rsrc.metadata_get()) self.m.VerifyAll() - def test_metadata_update_invalid(self): + def test_handle_signal_invalid(self): self.stack = self.create_stack() rsrc = self.stack['WaitHandle'] self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) - # metadata_update should raise a ValueError if the metadata + # handle_signal should raise a ValueError if the metadata # is missing any of the expected keys err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'data': 'foo', 'reason': 'bar', 'status': 'SUCCESS', 'uniqueid': '1234'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) # Also any Status other than SUCCESS or FAILURE should be rejected err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'UCCESS', 'UniqueId': '123'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'wibble', 'UniqueId': '123'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'success', 'UniqueId': '123'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'FAIL', 'UniqueId': '123'} - self.assertRaises(ValueError, rsrc.metadata_update, - new_metadata=err_metadata) + self.assertRaises(ValueError, rsrc.handle_signal, + err_metadata) self.m.VerifyAll() def test_get_status(self): @@ -501,12 +501,12 @@ class WaitConditionHandleTest(HeatTestCase): test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) self.assertEqual(['SUCCESS'], rsrc.get_status()) test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '456'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) self.assertEqual(['SUCCESS', 'SUCCESS'], rsrc.get_status()) def test_get_status_reason(self): @@ -516,17 +516,17 @@ class WaitConditionHandleTest(HeatTestCase): test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) self.assertEqual(['bar'], rsrc.get_status_reason('SUCCESS')) test_metadata = {'Data': 'dog', 'Reason': 'cat', 'Status': 'SUCCESS', 'UniqueId': '456'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) self.assertEqual(['bar', 'cat'], rsrc.get_status_reason('SUCCESS')) test_metadata = {'Data': 'boo', 'Reason': 'hoo', 'Status': 'FAILURE', 'UniqueId': '789'} - rsrc.metadata_update(new_metadata=test_metadata) + rsrc.handle_signal(test_metadata) self.assertEqual(['hoo'], rsrc.get_status_reason('FAILURE')) self.m.VerifyAll() @@ -587,7 +587,7 @@ class WaitConditionUpdateTest(HeatTestCase): wait_condition_handle = self.stack['WaitHandle'] test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '1'} - self._metadata_update(wait_condition_handle, test_metadata, 5) + self._handle_signal(wait_condition_handle, test_metadata, 5) uprops = copy.copy(rsrc.properties.data) uprops['Count'] = '5' @@ -614,7 +614,7 @@ class WaitConditionUpdateTest(HeatTestCase): wait_condition_handle = self.stack['WaitHandle'] test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '1'} - self._metadata_update(wait_condition_handle, test_metadata, 5) + self._handle_signal(wait_condition_handle, test_metadata, 5) prop_diff = {"Count": 5} props = copy.copy(rsrc.properties.data) @@ -641,13 +641,13 @@ class WaitConditionUpdateTest(HeatTestCase): wait_condition_handle = self.stack['WaitHandle'] test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '1'} - self._metadata_update(wait_condition_handle, test_metadata, 2) + self._handle_signal(wait_condition_handle, test_metadata, 2) self.stack.store() self.stack = self.get_stack(self.stack_id) rsrc = self.stack['WaitForTheHandle'] - self._metadata_update(wait_condition_handle, test_metadata, 3) + self._handle_signal(wait_condition_handle, test_metadata, 3) prop_diff = {"Count": 5} props = copy.copy(rsrc.properties.data) props.update(prop_diff) @@ -659,10 +659,10 @@ class WaitConditionUpdateTest(HeatTestCase): self.assertEqual(5, rsrc.properties['Count']) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) - def _metadata_update(self, rsrc, metadata, times=1): + def _handle_signal(self, rsrc, metadata, times=1): for time in range(times): metadata['UniqueId'] = metadata['UniqueId'] * 2 - rsrc.metadata_update(new_metadata=metadata) + rsrc.handle_signal(metadata) def test_handle_update_timeout(self): self.stack = self.create_stack()