From a272b3f8d913016a19f09c04c740e43fbd871d11 Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Fri, 13 Mar 2015 16:14:05 +0800 Subject: [PATCH] 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 --- .../openstack/heat/software_deployment.py | 22 ++++-- .../openstack/heat/structured_config.py | 3 + heat/tests/test_software_deployment.py | 74 +++++++++++++++++++ heat/tests/test_structured_config.py | 13 ++++ 4 files changed, 104 insertions(+), 8 deletions(-) diff --git a/heat/engine/resources/openstack/heat/software_deployment.py b/heat/engine/resources/openstack/heat/software_deployment.py index a171772afe..06cae5162f 100644 --- a/heat/engine/resources/openstack/heat/software_deployment.py +++ b/heat/engine/resources/openstack/heat/software_deployment.py @@ -219,12 +219,15 @@ class SoftwareDeployment(signal_responder.SignalResponder): return derived_config[rpc_api.SOFTWARE_CONFIG_ID] def _handle_action(self, action): - config_id = self.properties.get(self.CONFIG) - config = self.rpc_client().show_software_config( - self.context, config_id) + if self.properties.get(self.CONFIG): + config = self.rpc_client().show_software_config( + self.context, self.properties.get(self.CONFIG)) + else: + config = {} 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 props = self._build_properties( @@ -268,20 +271,23 @@ class SoftwareDeployment(signal_responder.SignalResponder): exc = exception.Error(message) raise exc + def empty_config(self): + return '' + def _build_derived_config_params(self, action, source): scl = sc.SoftwareConfig derived_inputs = self._build_derived_inputs(action, source) derived_options = self._build_derived_options(action, source) derived_config = self._build_derived_config( 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 { - scl.GROUP: source[scl.GROUP], - scl.CONFIG: derived_config, + scl.GROUP: source.get(scl.GROUP) or 'Heat::Ungrouped', + scl.CONFIG: derived_config or self.empty_config(), scl.OPTIONS: derived_options, scl.INPUTS: derived_inputs, 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, diff --git a/heat/engine/resources/openstack/heat/structured_config.py b/heat/engine/resources/openstack/heat/structured_config.py index 1d1b7a3314..9e5c940219 100644 --- a/heat/engine/resources/openstack/heat/structured_config.py +++ b/heat/engine/resources/openstack/heat/structured_config.py @@ -129,6 +129,9 @@ class StructuredDeployment(sd.SoftwareDeployment): ) } + def empty_config(self): + return {} + def _build_derived_config(self, action, source, derived_inputs, derived_options): cfg = source.get(sc.SoftwareConfig.CONFIG) diff --git a/heat/tests/test_software_deployment.py b/heat/tests/test_software_deployment.py index da641ed2a3..7493484c56 100644 --- a/heat/tests/test_software_deployment.py +++ b/heat/tests/test_software_deployment.py @@ -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): super(SoftwareDeploymentTest, self).setUp() self.ctx = utils.dummy_context() @@ -320,6 +334,66 @@ class SoftwareDeploymentTest(common.HeatTestCase): 'status_reason': 'Not waiting for outputs signal'}, 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): self._create_stack(self.template_no_signal) diff --git a/heat/tests/test_structured_config.py b/heat/tests/test_structured_config.py index c07802ba33..cbf92320bf 100644 --- a/heat/tests/test_structured_config.py +++ b/heat/tests/test_structured_config.py @@ -187,6 +187,19 @@ class StructuredDeploymentDerivedTest(common.HeatTestCase): 'CREATE', source, inputs, {}) 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):