From 7e1721dff3342721ee4ea69fe9e20b58b7426061 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Fri, 7 Nov 2014 14:53:46 +0100 Subject: [PATCH] Use RPC directly for software config operations This change replaces heatclient REST calls with RPC calls when creating, fetching or deleting software configs. All resources can now request and RPC client with self.rpc_client() which will create and memoize an EngineClient instance. Change-Id: Id23749e672fd4154e3d9986e22b46fc038579d8e Partial-blueprint: software-config-trigger --- heat/engine/resource.py | 8 ++ heat/engine/resources/server.py | 15 ++- .../resources/software_config/cloud_config.py | 5 +- .../resources/software_config/multi_part.py | 16 ++- .../software_config/software_component.py | 16 +-- .../software_config/software_config.py | 25 +++-- .../software_config/software_deployment.py | 31 ++++-- heat/tests/test_cloud_config.py | 12 +- heat/tests/test_multi_part.py | 34 +++--- heat/tests/test_server.py | 21 ++-- heat/tests/test_software_component.py | 44 +++----- heat/tests/test_software_config.py | 25 ++--- heat/tests/test_software_deployment.py | 104 +++++++++--------- heat/tests/test_structured_config.py | 12 +- 14 files changed, 192 insertions(+), 176 deletions(-) diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 49bf056aa0..8d640e8665 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -39,6 +39,7 @@ from heat.engine import rsrc_defn from heat.engine import scheduler from heat.engine import support from heat.openstack.common import log as logging +from heat.rpc import client as rpc_client cfg.CONF.import_opt('action_retry_limit', 'heat.common.config') @@ -177,11 +178,18 @@ class Resource(object): self._stored_properties_data = None self.created_time = None self.updated_time = None + self._rpc_client = None resource = stack.db_resource_get(name) if resource: self._load_data(resource) + def rpc_client(self): + '''Return a client for making engine RPC calls.''' + if not self._rpc_client: + self._rpc_client = rpc_client.EngineClient() + return self._rpc_client + def _load_data(self, resource): '''Load the resource state from its DB representation.''' self.resource_id = resource.nova_instance diff --git a/heat/engine/resources/server.py b/heat/engine/resources/server.py index f84d0d4dd0..ec0a7b0b0a 100644 --- a/heat/engine/resources/server.py +++ b/heat/engine/resources/server.py @@ -30,6 +30,7 @@ from heat.engine import stack_user from heat.engine import support from heat.openstack.common import log as logging from heat.openstack.common import uuidutils +from heat.rpc import api as rpc_api cfg.CONF.import_opt('instance_user', 'heat.common.config') @@ -469,6 +470,14 @@ class Server(stack_user.StackUser): return self.properties.get( self.SOFTWARE_CONFIG_TRANSPORT) == self.POLL_TEMP_URL + def get_software_config(self, ud_content): + try: + sc = self.rpc_client().show_software_config( + self.context, ud_content) + return sc[rpc_api.SOFTWARE_CONFIG_CONFIG] + except exception.NotFound: + return ud_content + def handle_create(self): security_groups = self.properties.get(self.SECURITY_GROUPS) @@ -477,11 +486,7 @@ class Server(stack_user.StackUser): if self.user_data_software_config() or self.user_data_raw(): if uuidutils.is_uuid_like(ud_content): # attempt to load the userdata from software config - try: - ud_content = self.heat().software_configs.get( - ud_content).config - except Exception as ex: - self.client_plugin('heat').ignore_not_found(ex) + ud_content = self.get_software_config(ud_content) metadata = self.metadata_get(True) or {} diff --git a/heat/engine/resources/software_config/cloud_config.py b/heat/engine/resources/software_config/cloud_config.py index c2dfac4747..0a928f4d14 100644 --- a/heat/engine/resources/software_config/cloud_config.py +++ b/heat/engine/resources/software_config/cloud_config.py @@ -17,6 +17,7 @@ from heat.common.template_format import yaml_dumper from heat.engine import properties from heat.engine.resources.software_config import software_config from heat.engine import support +from heat.rpc import api as rpc_api class CloudConfig(software_config.SoftwareConfig): @@ -54,8 +55,8 @@ class CloudConfig(software_config.SoftwareConfig): cloud_config = yaml.dump(self.properties.get( self.CLOUD_CONFIG), Dumper=yaml_dumper) props[self.CONFIG] = '#cloud-config\n%s' % cloud_config - sc = self.heat().software_configs.create(**props) - self.resource_id_set(sc.id) + sc = self.rpc_client().create_software_config(self.context, **props) + self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID]) def resource_mapping(): diff --git a/heat/engine/resources/software_config/multi_part.py b/heat/engine/resources/software_config/multi_part.py index 00567689d5..97be34dbb8 100644 --- a/heat/engine/resources/software_config/multi_part.py +++ b/heat/engine/resources/software_config/multi_part.py @@ -16,11 +16,13 @@ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import os +from heat.common import exception from heat.common.i18n import _ from heat.engine import constraints from heat.engine import properties from heat.engine.resources.software_config import software_config from heat.engine import support +from heat.rpc import api as rpc_api class MultipartMime(software_config.SoftwareConfig): @@ -95,8 +97,8 @@ class MultipartMime(software_config.SoftwareConfig): def handle_create(self): props = {self.NAME: self.physical_resource_name()} props[self.CONFIG] = self.get_message() - sc = self.heat().software_configs.create(**props) - self.resource_id_set(sc.id) + sc = self.rpc_client().create_software_config(self.context, **props) + self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID]) def get_message(self): if self.message: @@ -107,10 +109,14 @@ class MultipartMime(software_config.SoftwareConfig): config = item.get(self.CONFIG) part_type = item.get(self.TYPE, self.TEXT) part = config + try: - part = self.heat().software_configs.get(config).config - except Exception as ex: - self.client_plugin().ignore_not_found(ex) + sc = self.rpc_client().show_software_config( + self.context, self.resource_id) + except exception.NotFound: + pass + else: + part = sc[rpc_api.SOFTWARE_CONFIG_CONFIG] if part_type == self.MULTIPART: self._append_multiparts(subparts, part) diff --git a/heat/engine/resources/software_config/software_component.py b/heat/engine/resources/software_config/software_component.py index c4ff977018..40a7d8da6b 100644 --- a/heat/engine/resources/software_config/software_component.py +++ b/heat/engine/resources/software_config/software_component.py @@ -18,6 +18,7 @@ from heat.engine import properties from heat.engine import resource from heat.engine.resources.software_config import software_config as sc from heat.engine import support +from heat.rpc import api as rpc_api class SoftwareComponent(sc.SoftwareConfig): @@ -116,8 +117,8 @@ class SoftwareComponent(sc.SoftwareConfig): # set 'group' to enable component processing by in-instance hook props[self.GROUP] = 'component' - sc = self.heat().software_configs.create(**props) - self.resource_id_set(sc.id) + sc = self.rpc_client().create_software_config(self.context, **props) + self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID]) def _resolve_attribute(self, name): ''' @@ -129,14 +130,13 @@ class SoftwareComponent(sc.SoftwareConfig): ''' if name == self.CONFIGS_ATTR and self.resource_id: try: - config = self.heat().software_configs.get(self.resource_id).\ - config + sc = self.rpc_client().show_software_config( + self.context, self.resource_id) # configs list is stored in 'config' property of parent class # (see handle_create) - return config.get(self.CONFIGS) - except Exception as ex: - if self.client_plugin().is_not_found(ex): - return None + return sc[rpc_api.SOFTWARE_CONFIG_CONFIG].get(self.CONFIGS) + except exception.NotFound: + return None def validate(self): '''Validate SoftwareComponent properties consistency.''' diff --git a/heat/engine/resources/software_config/software_config.py b/heat/engine/resources/software_config/software_config.py index dff8960f8f..1ea7f830bf 100644 --- a/heat/engine/resources/software_config/software_config.py +++ b/heat/engine/resources/software_config/software_config.py @@ -11,6 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. +from heat.common import exception from heat.common.i18n import _ from heat.engine import attributes from heat.engine import constraints @@ -18,6 +19,7 @@ from heat.engine import properties from heat.engine import resource from heat.engine import support from heat.openstack.common import log as logging +from heat.rpc import api as rpc_api LOG = logging.getLogger(__name__) @@ -152,14 +154,12 @@ class SoftwareConfig(resource.Resource): ), } - default_client_name = 'heat' - def handle_create(self): props = dict(self.properties) props[self.NAME] = self.physical_resource_name() - sc = self.heat().software_configs.create(**props) - self.resource_id_set(sc.id) + sc = self.rpc_client().create_software_config(self.context, **props) + self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID]) def handle_delete(self): @@ -167,9 +167,10 @@ class SoftwareConfig(resource.Resource): return try: - self.heat().software_configs.delete(self.resource_id) - except Exception as ex: - self.client_plugin().ignore_not_found(ex) + self.rpc_client().delete_software_config( + self.context, self.resource_id) + except exception.NotFound: + pass def _resolve_attribute(self, name): ''' @@ -178,11 +179,11 @@ class SoftwareConfig(resource.Resource): ''' if name == self.CONFIG_ATTR and self.resource_id: try: - return self.heat().software_configs.get( - self.resource_id).config - except Exception as ex: - if self.client_plugin().is_not_found(ex): - return None + sc = self.rpc_client().show_software_config( + self.context, self.resource_id) + return sc[rpc_api.SOFTWARE_CONFIG_CONFIG] + except exception.NotFound: + return None def resource_mapping(): diff --git a/heat/engine/resources/software_config/software_deployment.py b/heat/engine/resources/software_config/software_deployment.py index 1873ba6374..a49ab6bb4b 100644 --- a/heat/engine/resources/software_config/software_deployment.py +++ b/heat/engine/resources/software_config/software_deployment.py @@ -26,6 +26,7 @@ from heat.engine.resources.software_config import software_config as sc from heat.engine import signal_responder from heat.engine import support from heat.openstack.common import log as logging +from heat.rpc import api as rpc_api LOG = logging.getLogger(__name__) @@ -194,23 +195,26 @@ class SoftwareDeployment(signal_responder.SignalResponder): def _delete_derived_config(self, derived_config_id): try: - self.heat().software_configs.delete(derived_config_id) - except Exception as ex: - self.client_plugin().ignore_not_found(ex) + self.rpc_client().delete_software_config( + self.context, derived_config_id) + except exception.NotFound: + pass def _get_derived_config(self, action, source_config): derived_params = self._build_derived_config_params( - action, source_config.to_dict()) - derived_config = self.heat().software_configs.create(**derived_params) - return derived_config.id + action, source_config) + derived_config = self.rpc_client().create_software_config( + self.context, **derived_params) + return derived_config[rpc_api.SOFTWARE_CONFIG_ID] def _handle_action(self, action): config_id = self.properties.get(self.CONFIG) - config = self.heat().software_configs.get(config_id) + config = self.rpc_client().show_software_config( + self.context, config_id) if action not in self.properties[self.DEPLOY_ACTIONS]\ - and not config.group == 'component': + and not config[rpc_api.SOFTWARE_CONFIG_GROUP] == 'component': return props = self._build_properties( @@ -430,7 +434,8 @@ class SoftwareDeployment(signal_responder.SignalResponder): def handle_signal(self, details): sd = self.heat().software_deployments.get(self.resource_id) - sc = self.heat().software_configs.get(self.properties[self.CONFIG]) + sc = self.rpc_client().show_software_config( + self.context, self.properties[self.CONFIG]) if not sd.status == self.IN_PROGRESS: # output values are only expected when in an IN_PROGRESS state return @@ -450,7 +455,7 @@ class SoftwareDeployment(signal_responder.SignalResponder): else: event_reason = 'deployment succeeded' - for output in sc.outputs or []: + for output in sc[rpc_api.SOFTWARE_CONFIG_OUTPUTS] or []: out_key = output['name'] if out_key in details: ov[out_key] = details[out_key] @@ -486,8 +491,10 @@ class SoftwareDeployment(signal_responder.SignalResponder): # Since there is no value for this key yet, check the output schemas # to find out if the key is valid - sc = self.heat().software_configs.get(self.properties[self.CONFIG]) - output_keys = [output['name'] for output in sc.outputs] + sc = self.rpc_client().show_software_config( + self.context, self.properties[self.CONFIG]) + outputs = sc[rpc_api.SOFTWARE_CONFIG_OUTPUTS] or [] + output_keys = [output['name'] for output in outputs] if key not in output_keys and key not in self.ATTRIBUTES: raise exception.InvalidTemplateAttribute(resource=self.name, key=key) diff --git a/heat/tests/test_cloud_config.py b/heat/tests/test_cloud_config.py index 5164158423..0f6eb529e8 100644 --- a/heat/tests/test_cloud_config.py +++ b/heat/tests/test_cloud_config.py @@ -38,9 +38,8 @@ class CloudConfigTest(common.HeatTestCase): 'Properties': self.properties }}})) self.config = self.stack['config_mysql'] - heat = mock.MagicMock() - self.config.heat = heat - self.software_configs = heat.return_value.software_configs + self.rpc_client = mock.MagicMock() + self.config._rpc_client = self.rpc_client def test_resource_mapping(self): mapping = cc.resource_mapping() @@ -50,11 +49,10 @@ class CloudConfigTest(common.HeatTestCase): self.assertIsInstance(self.config, cc.CloudConfig) def test_handle_create(self): - sc = mock.MagicMock() config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - sc.id = config_id - self.software_configs.create.return_value = sc + value = {'id': config_id} + self.rpc_client.create_software_config.return_value = value self.config.handle_create() self.assertEqual(config_id, self.config.resource_id) - kwargs = self.software_configs.create.call_args[1] + kwargs = self.rpc_client.create_software_config.call_args[1] self.assertEqual('#cloud-config\n{foo: bar}\n', kwargs['config']) diff --git a/heat/tests/test_multi_part.py b/heat/tests/test_multi_part.py index efde788916..f7ee02c06f 100644 --- a/heat/tests/test_multi_part.py +++ b/heat/tests/test_multi_part.py @@ -13,9 +13,9 @@ import email -import heatclient.exc as exc import mock +from heat.common import exception as exc from heat.engine import parser from heat.engine.resources.software_config import multi_part as mp from heat.engine import template @@ -43,9 +43,8 @@ class MultipartMimeTest(common.HeatTestCase): 'parts': parts }}}})) self.config = stack['config_mysql'] - heat = mock.MagicMock() - self.config.heat = heat - self.software_configs = heat.return_value.software_configs + self.rpc_client = mock.MagicMock() + self.config._rpc_client = self.rpc_client def test_resource_mapping(self): mapping = mp.resource_mapping() @@ -55,13 +54,12 @@ class MultipartMimeTest(common.HeatTestCase): self.assertIsInstance(self.config, mp.MultipartMime) def test_handle_create(self): - sc = mock.MagicMock() config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - sc.id = config_id - self.software_configs.create.return_value = sc + sc = {'id': config_id} + self.rpc_client.create_software_config.return_value = sc self.config.handle_create() self.assertEqual(config_id, self.config.resource_id) - args = self.software_configs.create.call_args[1] + args = self.rpc_client.create_software_config.call_args[1] self.assertEqual(self.config.message, args['config']) def test_get_message_not_none(self): @@ -82,7 +80,9 @@ class MultipartMimeTest(common.HeatTestCase): 'type': 'text' }] self.init_config(parts=parts) - self.software_configs.get.return_value.config = '#!/bin/bash' + self.rpc_client.show_software_config.return_value = { + 'config': '#!/bin/bash' + } result = self.config.get_message() message = email.message_from_string(result) self.assertTrue(message.is_multipart()) @@ -96,7 +96,7 @@ class MultipartMimeTest(common.HeatTestCase): 'type': 'text' }] self.init_config(parts=parts) - self.software_configs.get.side_effect = exc.HTTPNotFound() + self.rpc_client.show_software_config.side_effect = exc.NotFound() result = self.config.get_message() message = email.message_from_string(result) self.assertTrue(message.is_multipart()) @@ -111,7 +111,9 @@ class MultipartMimeTest(common.HeatTestCase): 'filename': '/opt/stack/configure.d/55-heat-config' }] self.init_config(parts=parts) - self.software_configs.get.return_value.config = '#!/bin/bash' + self.rpc_client.show_software_config.return_value = { + 'config': '#!/bin/bash' + } result = self.config.get_message() message = email.message_from_string(result) self.assertTrue(message.is_multipart()) @@ -138,7 +140,11 @@ class MultipartMimeTest(common.HeatTestCase): 'type': 'multipart' }] self.init_config(parts=parts) - self.software_configs.get.return_value.config = multipart + + self.rpc_client.show_software_config.return_value = { + 'config': multipart + } + result = self.config.get_message() message = email.message_from_string(result) self.assertTrue(message.is_multipart()) @@ -155,7 +161,9 @@ class MultipartMimeTest(common.HeatTestCase): {'config': '9cab10ef-16ce-4be9-8b25-a67b7313eddb', 'type': 'text'}] self.init_config(parts=parts) - self.software_configs.get.return_value.config = '#!/bin/bash' + self.rpc_client.show_software_config.return_value = { + 'config': '#!/bin/bash' + } result = self.config.get_message() message = email.message_from_string(result) self.assertTrue(message.is_multipart()) diff --git a/heat/tests/test_server.py b/heat/tests/test_server.py index 7d8081730d..646fcc83be 100644 --- a/heat/tests/test_server.py +++ b/heat/tests/test_server.py @@ -26,7 +26,6 @@ from heat.common.i18n import _ from heat.common import template_format from heat.db import api as db_api from heat.engine.clients.os import glance -from heat.engine.clients.os import heat_plugin from heat.engine.clients.os import nova from heat.engine.clients.os import swift from heat.engine import environment @@ -531,12 +530,11 @@ class ServersTest(common.HeatTestCase): server = servers.Server('WebServer', resource_defns['WebServer'], stack) - self.m.StubOutWithMock(heat_plugin.HeatClientPlugin, '_create') - heat_client = mock.Mock() - heat_plugin.HeatClientPlugin._create().AndReturn(heat_client) - sc = mock.Mock() - sc.config = 'wordpress from config' - heat_client.software_configs.get.return_value = sc + self.rpc_client = mock.MagicMock() + server._rpc_client = self.rpc_client + + sc = {'config': 'wordpress from config'} + self.rpc_client.show_software_config.return_value = sc self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') nova.NovaClientPlugin._create().AndReturn(self.fc) @@ -572,11 +570,10 @@ class ServersTest(common.HeatTestCase): server = servers.Server('WebServer', resource_defns['WebServer'], stack) - self.m.StubOutWithMock(heat_plugin.HeatClientPlugin, '_create') - heat_client = mock.Mock() - heat_plugin.HeatClientPlugin._create().AndReturn(heat_client) - heat_client.software_configs.get.side_effect = \ - heat_plugin.exc.HTTPNotFound() + self.rpc_client = mock.MagicMock() + server._rpc_client = self.rpc_client + + self.rpc_client.show_software_config.side_effect = exception.NotFound self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') nova.NovaClientPlugin._create().AndReturn(self.fc) diff --git a/heat/tests/test_software_component.py b/heat/tests/test_software_component.py index 7bf4154cd5..1b6d072cfe 100644 --- a/heat/tests/test_software_component.py +++ b/heat/tests/test_software_component.py @@ -14,14 +14,13 @@ import mock import six -from heat.common import exception +from heat.common import exception as exc from heat.common import template_format from heat.engine.resources.software_config import software_component as sc from heat.engine import stack from heat.engine import template from heat.tests import common from heat.tests import utils -from heatclient.exc import HTTPNotFound class SoftwareComponentTest(common.HeatTestCase): @@ -58,11 +57,8 @@ class SoftwareComponentTest(common.HeatTestCase): self.ctx, 'software_component_test_stack', template.Template(self.template)) self.component = self.stack['mysql_component'] - heat = mock.MagicMock() - self.heatclient = mock.MagicMock() - self.component.heat = heat - heat.return_value = self.heatclient - self.software_configs = self.heatclient.software_configs + self.rpc_client = mock.MagicMock() + self.component._rpc_client = self.rpc_client def test_resource_mapping(self): mapping = sc.resource_mapping() @@ -72,10 +68,9 @@ class SoftwareComponentTest(common.HeatTestCase): self.assertIsInstance(self.component, sc.SoftwareComponent) def test_handle_create(self): - value = mock.MagicMock() config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - value.id = config_id - self.software_configs.create.return_value = value + value = {'id': config_id} + self.rpc_client.create_software_config.return_value = value self.component.handle_create() self.assertEqual(config_id, self.component.resource_id) @@ -84,9 +79,9 @@ class SoftwareComponentTest(common.HeatTestCase): self.assertIsNone(self.component.handle_delete()) config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' self.component.resource_id = config_id - self.software_configs.delete.return_value = None + self.rpc_client.delete_software_config.return_value = None self.assertIsNone(self.component.handle_delete()) - self.software_configs.delete.side_effect = HTTPNotFound() + self.rpc_client.delete_software_config.side_effect = exc.NotFound self.assertIsNone(self.component.handle_delete()) def test_resolve_attribute(self): @@ -94,14 +89,13 @@ class SoftwareComponentTest(common.HeatTestCase): self.component.resource_id = None self.assertIsNone(self.component._resolve_attribute('configs')) self.component.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - value = mock.MagicMock() configs = self.\ template['resources']['mysql_component']['properties']['configs'] # configs list is stored in 'config' property of SoftwareConfig - value.config = {'configs': configs} - self.software_configs.get.return_value = value + value = {'config': {'configs': configs}} + self.rpc_client.show_software_config.return_value = value self.assertEqual(configs, self.component._resolve_attribute('configs')) - self.software_configs.get.side_effect = HTTPNotFound() + self.rpc_client.show_software_config.side_effect = exc.NotFound self.assertIsNone(self.component._resolve_attribute('configs')) @@ -160,7 +154,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): echo CREATE $foo tool: script ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='Unknown Property config') ), ( @@ -172,7 +166,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): inputs: - name: foo ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='Property configs not assigned') ), # do not test until bug #1350840 @@ -199,7 +193,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): config: #!/bin/bash tool: script ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='is not a list') ), ( @@ -213,7 +207,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): config: #!/bin/bash tool: script ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='actions length (0) is out of range ' '(min: 1, max: None)') ), @@ -231,7 +225,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): config: #!/bin/bash tool: script ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='Defining more than one configuration for the same ' 'action in SoftwareComponent "component" is not ' 'allowed.') @@ -250,7 +244,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): config: #!/bin/bash tool: script ''', - err=exception.StackValidationFailed, + err=exc.StackValidationFailed, err_msg='Defining more than one configuration for the same ' 'action in SoftwareComponent "component" is not ' 'allowed.') @@ -272,11 +266,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase): self.ctx, 'software_component_test_stack', template.Template(self.template)) self.component = self.stack['component'] - heat = mock.MagicMock() - self.heatclient = mock.MagicMock() - self.component.heat = heat - heat.return_value = self.heatclient - self.software_configs = self.heatclient.software_configs + self.component._rpc_client = mock.MagicMock() def test_properties_schema(self): if self.err: diff --git a/heat/tests/test_software_config.py b/heat/tests/test_software_config.py index 3e6a9effd2..b2a62a3819 100644 --- a/heat/tests/test_software_config.py +++ b/heat/tests/test_software_config.py @@ -11,9 +11,9 @@ # License for the specific language governing permissions and limitations # under the License. -from heatclient.exc import HTTPNotFound import mock +from heat.common import exception as exc from heat.engine import parser from heat.engine.resources.software_config import software_config as sc from heat.engine import template @@ -43,11 +43,8 @@ class SoftwareConfigTest(common.HeatTestCase): 'Properties': self.properties }}})) self.config = self.stack['config_mysql'] - heat = mock.MagicMock() - self.heatclient = mock.MagicMock() - self.config.heat = heat - heat.return_value = self.heatclient - self.software_configs = self.heatclient.software_configs + self.rpc_client = mock.MagicMock() + self.config._rpc_client = self.rpc_client def test_resource_mapping(self): mapping = sc.resource_mapping() @@ -57,10 +54,9 @@ class SoftwareConfigTest(common.HeatTestCase): self.assertIsInstance(self.config, sc.SoftwareConfig) def test_handle_create(self): - value = mock.MagicMock() config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - value.id = config_id - self.software_configs.create.return_value = value + value = {'id': config_id} + self.rpc_client.create_software_config.return_value = value self.config.handle_create() self.assertEqual(config_id, self.config.resource_id) @@ -69,9 +65,9 @@ class SoftwareConfigTest(common.HeatTestCase): self.assertIsNone(self.config.handle_delete()) config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' self.config.resource_id = config_id - self.software_configs.delete.return_value = None + self.rpc_client.delete_software_config.return_value = None self.assertIsNone(self.config.handle_delete()) - self.software_configs.delete.side_effect = HTTPNotFound() + self.rpc_client.delete_software_config.side_effect = exc.NotFound self.assertIsNone(self.config.handle_delete()) def test_resolve_attribute(self): @@ -79,10 +75,9 @@ class SoftwareConfigTest(common.HeatTestCase): self.config.resource_id = None self.assertIsNone(self.config._resolve_attribute('config')) self.config.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - value = mock.MagicMock() - value.config = '#!/bin/bash' - self.software_configs.get.return_value = value + value = {'config': '#!/bin/bash'} + self.rpc_client.show_software_config.return_value = value self.assertEqual( '#!/bin/bash', self.config._resolve_attribute('config')) - self.software_configs.get.side_effect = HTTPNotFound() + self.rpc_client.show_software_config.side_effect = exc.NotFound self.assertEqual(None, self.config._resolve_attribute('config')) diff --git a/heat/tests/test_software_deployment.py b/heat/tests/test_software_deployment.py index 11db5081be..ba9b3a14da 100644 --- a/heat/tests/test_software_deployment.py +++ b/heat/tests/test_software_deployment.py @@ -17,7 +17,7 @@ import copy import mock import six -from heat.common import exception +from heat.common import exception as exc from heat.common.i18n import _ from heat.engine import parser from heat.engine.resources.software_config import software_deployment as sd @@ -117,10 +117,13 @@ class SoftwareDeploymentTest(common.HeatTestCase): get_signed_url.return_value = 'http://192.0.2.2/signed_url' self.deployment = self.stack['deployment_mysql'] + + self.rpc_client = mock.MagicMock() + self.deployment._rpc_client = self.rpc_client + heat = mock.MagicMock() self.deployment.heat = heat self.deployments = heat.return_value.software_deployments - self.software_configs = heat.return_value.software_configs def test_validate(self): template = dict(self.template_with_server) @@ -138,7 +141,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): props['user_data_format'] = 'RAW' self._create_stack(template) sd = self.deployment - err = self.assertRaises(exception.StackValidationFailed, sd.validate) + err = self.assertRaises(exc.StackValidationFailed, sd.validate) self.assertEqual("Resource server's property " "user_data_format should be set to " "SOFTWARE_CONFIG since there are " @@ -149,7 +152,6 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.assertIsInstance(self.deployment, sd.SoftwareDeployment) def mock_software_config(self): - sc = mock.MagicMock() config = { 'id': '48e8ade1-9196-42d5-89a2-f709fde42632', 'group': 'Test::Group', @@ -167,14 +169,10 @@ class SoftwareDeploymentTest(common.HeatTestCase): }], 'outputs': [], } - sc.to_dict.return_value = config - sc.group = 'Test::Group' - sc.config = config['config'] - self.software_configs.get.return_value = sc - return sc + self.rpc_client.show_software_config.return_value = config + return config def mock_software_component(self): - sc = mock.MagicMock() config = { 'id': '48e8ade1-9196-42d5-89a2-f709fde42632', 'group': 'component', @@ -220,16 +218,12 @@ class SoftwareDeploymentTest(common.HeatTestCase): }], 'outputs': [], } - sc.to_dict.return_value = config - sc.group = 'component' - sc.config = config['config'] - self.software_configs.get.return_value = sc - return sc + self.rpc_client.show_software_config.return_value = config + return config def mock_derived_software_config(self): - sc = mock.MagicMock() - sc.id = '9966c8e7-bc9c-42de-aa7d-f2447a952cb2' - self.software_configs.create.return_value = sc + sc = {'id': '9966c8e7-bc9c-42de-aa7d-f2447a952cb2'} + self.rpc_client.create_software_config.return_value = sc return sc def mock_deployment(self): @@ -290,11 +284,11 @@ class SoftwareDeploymentTest(common.HeatTestCase): }], 'options': {}, 'outputs': [] - }, self.software_configs.create.call_args[1]) + }, self.rpc_client.create_software_config.call_args[1]) self.assertEqual( {'action': 'CREATE', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'COMPLETE', @@ -381,11 +375,11 @@ class SoftwareDeploymentTest(common.HeatTestCase): }], 'options': {}, 'outputs': [] - }, self.software_configs.create.call_args[1]) + }, self.rpc_client.create_software_config.call_args[1]) self.assertEqual( {'action': 'CREATE', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'COMPLETE', @@ -404,7 +398,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): args = self.deployments.create.call_args[1] self.assertEqual( {'action': 'CREATE', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'IN_PROGRESS', @@ -449,7 +443,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): sd.status = self.deployment.FAILED sd.status_reason = 'something wrong' err = self.assertRaises( - exception.Error, self.deployment.check_create_complete, sd) + exc.Error, self.deployment.check_create_complete, sd) self.assertEqual( 'Deployment to server failed: something wrong', six.text_type(err)) @@ -479,7 +473,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): args = sd.update.call_args[1] self.assertEqual({ 'action': 'DELETE', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'IN_PROGRESS', @@ -499,14 +493,15 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.mock_software_config() derived_sc = self.mock_derived_software_config() sd = self.mock_deployment() - sd.config_id = derived_sc.id + sd.config_id = derived_sc['id'] self.deployments.get.return_value = sd sd.delete.side_effect = HTTPNotFound() - self.software_configs.delete.side_effect = HTTPNotFound() + self.rpc_client.delete_software_config.side_effect = exc.NotFound self.assertIsNone(self.deployment.handle_delete()) self.assertEqual( - (derived_sc.id,), self.software_configs.delete.call_args[0]) + (self.ctx, derived_sc['id']), + self.rpc_client.delete_software_config.call_args[0]) def test_handle_delete_none(self): self._create_stack(self.template) @@ -539,13 +534,14 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.deployment.handle_update( json_snippet=snippet, tmpl_diff=None, prop_diff=prop_diff) self.assertEqual( - (config_id,), self.software_configs.get.call_args[0]) + (self.ctx, config_id), + self.rpc_client.show_software_config.call_args[0]) args = self.deployments.get.call_args[0] self.assertEqual(1, len(args)) self.assertIn(sd.id, args) args = sd.update.call_args[1] - self.assertEqual(derived_sc.id, args['config_id']) + self.assertEqual(derived_sc['id'], args['config_id']) def test_handle_suspend_resume(self): self._create_stack(self.template_delete_suspend_resume) @@ -564,7 +560,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): args = sd.update.call_args[1] self.assertEqual({ 'action': 'SUSPEND', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'IN_PROGRESS', @@ -583,7 +579,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): args = sd.update.call_args[1] self.assertEqual({ 'action': 'RESUME', - 'config_id': derived_sc.id, + 'config_id': derived_sc['id'], 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'status': 'IN_PROGRESS', @@ -599,16 +595,18 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_handle_signal_ok_zero(self): self._create_stack(self.template) sd = mock.MagicMock() - sc = mock.MagicMock() - sc.outputs = [{'name': 'foo'}, - {'name': 'foo2'}, - {'name': 'failed', - 'error_output': True}] + sc = { + 'outputs': [ + {'name': 'foo'}, + {'name': 'foo2'}, + {'name': 'failed', 'error_output': True} + ] + } sd.output_values = {} sd.status = self.deployment.IN_PROGRESS sd.update.return_value = None self.deployments.get.return_value = sd - self.software_configs.get.return_value = sc + self.rpc_client.show_software_config.return_value = sc details = { 'foo': 'bar', 'deploy_status_code': 0 @@ -630,16 +628,18 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_handle_signal_ok_str_zero(self): self._create_stack(self.template) sd = mock.MagicMock() - sc = mock.MagicMock() - sc.outputs = [{'name': 'foo'}, - {'name': 'foo2'}, - {'name': 'failed', - 'error_output': True}] + sc = { + 'outputs': [ + {'name': 'foo'}, + {'name': 'foo2'}, + {'name': 'failed', 'error_output': True} + ] + } sd.output_values = {} sd.status = self.deployment.IN_PROGRESS sd.update.return_value = None self.deployments.get.return_value = sd - self.software_configs.get.return_value = sc + self.rpc_client.show_software_config.return_value = sc details = { 'foo': 'bar', 'deploy_status_code': '0' @@ -661,16 +661,18 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_handle_signal_failed(self): self._create_stack(self.template) sd = mock.MagicMock() - sc = mock.MagicMock() - sc.outputs = [{'name': 'foo'}, - {'name': 'foo2'}, - {'name': 'failed', - 'error_output': True}] + sc = { + 'outputs': [ + {'name': 'foo'}, + {'name': 'foo2'}, + {'name': 'failed', 'error_output': True} + ] + } sd.output_values = {} sd.status = self.deployment.IN_PROGRESS sd.update.return_value = None self.deployments.get.return_value = sd - self.software_configs.get.return_value = sc + self.rpc_client.show_software_config.return_value = sc details = {'failed': 'no enough memory found.'} ret = self.deployment.handle_signal(details) self.assertEqual('deployment failed', ret) @@ -760,7 +762,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): sd.outputs = [] sd.output_values = {'foo': 'bar'} err = self.assertRaises( - exception.InvalidTemplateAttribute, + exc.InvalidTemplateAttribute, self.deployment.FnGetAtt, 'foo2') self.assertEqual( 'The Referenced Attribute (deployment_mysql foo2) is incorrect.', diff --git a/heat/tests/test_structured_config.py b/heat/tests/test_structured_config.py index 87e6e2d033..c0645b1436 100644 --- a/heat/tests/test_structured_config.py +++ b/heat/tests/test_structured_config.py @@ -44,9 +44,8 @@ class StructuredConfigTestJSON(common.HeatTestCase): self.ctx, 'software_config_test_stack', template.Template(self.template)) self.config = self.stack['config_mysql'] - heat = mock.MagicMock() - self.config.heat = heat - self.software_configs = heat.return_value.software_configs + self.rpc_client = mock.MagicMock() + self.config._rpc_client = self.rpc_client def test_resource_mapping(self): mapping = sc.resource_mapping() @@ -60,13 +59,12 @@ class StructuredConfigTestJSON(common.HeatTestCase): self.assertIsInstance(self.config, sc.StructuredConfig) def test_handle_create(self): - stc = mock.MagicMock() config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - stc.id = config_id - self.software_configs.create.return_value = stc + value = {'id': config_id} + self.rpc_client.create_software_config.return_value = value self.config.handle_create() self.assertEqual(config_id, self.config.resource_id) - kwargs = self.software_configs.create.call_args[1] + kwargs = self.rpc_client.create_software_config.call_args[1] self.assertEqual(self.stored_config, kwargs['config'])