Allow empty 'config' for software deployments

Allow empty 'config' for software deployment resources.
The use case would be for custom agents where the config
is already baked into the image, so these agents would
still want to:
  read input_values to feed into the baked-in config
  signal heat that deployment is complete, including output values

Change-Id: I87807065bf5821bcff23810d7e58c34eaa991f19
Closes-Bug: #1407387
This commit is contained in:
huangtianhua 2015-03-13 16:14:05 +08:00
parent 115d35f930
commit a272b3f8d9
4 changed files with 104 additions and 8 deletions

View File

@ -219,12 +219,15 @@ class SoftwareDeployment(signal_responder.SignalResponder):
return derived_config[rpc_api.SOFTWARE_CONFIG_ID] return derived_config[rpc_api.SOFTWARE_CONFIG_ID]
def _handle_action(self, action): def _handle_action(self, action):
config_id = self.properties.get(self.CONFIG) if self.properties.get(self.CONFIG):
config = self.rpc_client().show_software_config( config = self.rpc_client().show_software_config(
self.context, config_id) self.context, self.properties.get(self.CONFIG))
else:
config = {}
if (action not in self.properties[self.DEPLOY_ACTIONS] if (action not in self.properties[self.DEPLOY_ACTIONS]
and not config[rpc_api.SOFTWARE_CONFIG_GROUP] == 'component'): and not config.get(
rpc_api.SOFTWARE_CONFIG_GROUP) == 'component'):
return return
props = self._build_properties( props = self._build_properties(
@ -268,20 +271,23 @@ class SoftwareDeployment(signal_responder.SignalResponder):
exc = exception.Error(message) exc = exception.Error(message)
raise exc raise exc
def empty_config(self):
return ''
def _build_derived_config_params(self, action, source): def _build_derived_config_params(self, action, source):
scl = sc.SoftwareConfig scl = sc.SoftwareConfig
derived_inputs = self._build_derived_inputs(action, source) derived_inputs = self._build_derived_inputs(action, source)
derived_options = self._build_derived_options(action, source) derived_options = self._build_derived_options(action, source)
derived_config = self._build_derived_config( derived_config = self._build_derived_config(
action, source, derived_inputs, derived_options) action, source, derived_inputs, derived_options)
derived_name = self.properties.get(self.NAME) or source[scl.NAME] derived_name = self.properties.get(self.NAME) or source.get(scl.NAME)
return { return {
scl.GROUP: source[scl.GROUP], scl.GROUP: source.get(scl.GROUP) or 'Heat::Ungrouped',
scl.CONFIG: derived_config, scl.CONFIG: derived_config or self.empty_config(),
scl.OPTIONS: derived_options, scl.OPTIONS: derived_options,
scl.INPUTS: derived_inputs, scl.INPUTS: derived_inputs,
scl.OUTPUTS: source.get(scl.OUTPUTS), scl.OUTPUTS: source.get(scl.OUTPUTS),
scl.NAME: derived_name scl.NAME: derived_name or self.physical_resource_name()
} }
def _build_derived_config(self, action, source, def _build_derived_config(self, action, source,

View File

@ -129,6 +129,9 @@ class StructuredDeployment(sd.SoftwareDeployment):
) )
} }
def empty_config(self):
return {}
def _build_derived_config(self, action, source, def _build_derived_config(self, action, source,
derived_inputs, derived_options): derived_inputs, derived_options):
cfg = source.get(sc.SoftwareConfig.CONFIG) cfg = source.get(sc.SoftwareConfig.CONFIG)

View File

@ -115,6 +115,20 @@ class SoftwareDeploymentTest(common.HeatTestCase):
} }
} }
template_no_config = {
'HeatTemplateFormatVersion': '2012-12-12',
'Resources': {
'deployment_mysql': {
'Type': 'OS::Heat::SoftwareDeployment',
'Properties': {
'server': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'input_values': {'foo': 'bar', 'bink': 'bonk'},
'signal_transport': 'NO_SIGNAL',
}
}
}
}
def setUp(self): def setUp(self):
super(SoftwareDeploymentTest, self).setUp() super(SoftwareDeploymentTest, self).setUp()
self.ctx = utils.dummy_context() self.ctx = utils.dummy_context()
@ -320,6 +334,66 @@ class SoftwareDeploymentTest(common.HeatTestCase):
'status_reason': 'Not waiting for outputs signal'}, 'status_reason': 'Not waiting for outputs signal'},
self.rpc_client.create_software_deployment.call_args[1]) self.rpc_client.create_software_deployment.call_args[1])
def test_handle_create_without_config(self):
self._create_stack(self.template_no_config)
sd = self.mock_deployment()
derived_sc = self.mock_derived_software_config()
self.deployment.handle_create()
self.assertEqual(sd['id'], self.deployment.resource_id)
self.assertEqual({
'config': '',
'group': 'Heat::Ungrouped',
'name': self.deployment.physical_resource_name(),
'inputs': [{
'name': 'foo',
'type': 'String',
'value': 'bar'
}, {
'name': 'bink',
'type': 'String',
'value': 'bonk'
}, {
'description': 'ID of the server being deployed to',
'name': 'deploy_server_id',
'type': 'String',
'value': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0'
}, {
'description': 'Name of the current action being deployed',
'name': 'deploy_action',
'type': 'String',
'value': 'CREATE'
}, {
'description': 'ID of the stack this deployment belongs to',
'name': 'deploy_stack_id',
'type': 'String',
'value': ('software_deployment_test_stack'
'/42f6f66b-631a-44e7-8d01-e22fb54574a9')
}, {
'description': 'Name of this deployment resource in the stack',
'name': 'deploy_resource_name',
'type': 'String',
'value': 'deployment_mysql'
}, {
'description': ('How the server should signal to heat with '
'the deployment output values.'),
'name': 'deploy_signal_transport',
'type': 'String',
'value': 'NO_SIGNAL'
}],
'options': None,
'outputs': None
}, self.rpc_client.create_software_config.call_args[1])
self.assertEqual(
{'action': 'CREATE',
'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'COMPLETE',
'status_reason': 'Not waiting for outputs signal'},
self.rpc_client.create_software_deployment.call_args[1])
def test_handle_create_for_component(self): def test_handle_create_for_component(self):
self._create_stack(self.template_no_signal) self._create_stack(self.template_no_signal)

View File

@ -187,6 +187,19 @@ class StructuredDeploymentDerivedTest(common.HeatTestCase):
'CREATE', source, inputs, {}) 'CREATE', source, inputs, {})
self.assertEqual({"foo": "baz"}, result) self.assertEqual({"foo": "baz"}, result)
def test_build_derived_config_params_with_empty_config(self):
source = {}
result = self.deployment._build_derived_config_params(
'CREATE', source)
self.assertEqual('Heat::Ungrouped', result['group'])
self.assertEqual({}, result['config'])
self.assertEqual(self.deployment.physical_resource_name(),
result['name'])
self.assertIn({'name': 'bar', 'type': 'String', 'value': 'baz'},
result['inputs'])
self.assertIsNone(result['options'])
self.assertIsNone(result['outputs'])
class StructuredDeploymentWithStrictInputTest(common.HeatTestCase): class StructuredDeploymentWithStrictInputTest(common.HeatTestCase):