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
This commit is contained in:
Steven Hardy 2014-06-19 14:41:09 +01:00
parent f28c983409
commit 02b6d5f984
4 changed files with 53 additions and 42 deletions

View File

@ -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)

View File

@ -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):
"""

View File

@ -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,

View File

@ -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()