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
This commit is contained in:
Steve Baker 2014-11-07 14:53:46 +01:00
parent bf437d3372
commit 7e1721dff3
14 changed files with 192 additions and 176 deletions

View File

@ -39,6 +39,7 @@ from heat.engine import rsrc_defn
from heat.engine import scheduler from heat.engine import scheduler
from heat.engine import support from heat.engine import support
from heat.openstack.common import log as logging 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') cfg.CONF.import_opt('action_retry_limit', 'heat.common.config')
@ -177,11 +178,18 @@ class Resource(object):
self._stored_properties_data = None self._stored_properties_data = None
self.created_time = None self.created_time = None
self.updated_time = None self.updated_time = None
self._rpc_client = None
resource = stack.db_resource_get(name) resource = stack.db_resource_get(name)
if resource: if resource:
self._load_data(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): def _load_data(self, resource):
'''Load the resource state from its DB representation.''' '''Load the resource state from its DB representation.'''
self.resource_id = resource.nova_instance self.resource_id = resource.nova_instance

View File

@ -30,6 +30,7 @@ from heat.engine import stack_user
from heat.engine import support from heat.engine import support
from heat.openstack.common import log as logging from heat.openstack.common import log as logging
from heat.openstack.common import uuidutils from heat.openstack.common import uuidutils
from heat.rpc import api as rpc_api
cfg.CONF.import_opt('instance_user', 'heat.common.config') cfg.CONF.import_opt('instance_user', 'heat.common.config')
@ -469,6 +470,14 @@ class Server(stack_user.StackUser):
return self.properties.get( return self.properties.get(
self.SOFTWARE_CONFIG_TRANSPORT) == self.POLL_TEMP_URL 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): def handle_create(self):
security_groups = self.properties.get(self.SECURITY_GROUPS) 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 self.user_data_software_config() or self.user_data_raw():
if uuidutils.is_uuid_like(ud_content): if uuidutils.is_uuid_like(ud_content):
# attempt to load the userdata from software config # attempt to load the userdata from software config
try: ud_content = self.get_software_config(ud_content)
ud_content = self.heat().software_configs.get(
ud_content).config
except Exception as ex:
self.client_plugin('heat').ignore_not_found(ex)
metadata = self.metadata_get(True) or {} metadata = self.metadata_get(True) or {}

View File

@ -17,6 +17,7 @@ from heat.common.template_format import yaml_dumper
from heat.engine import properties from heat.engine import properties
from heat.engine.resources.software_config import software_config from heat.engine.resources.software_config import software_config
from heat.engine import support from heat.engine import support
from heat.rpc import api as rpc_api
class CloudConfig(software_config.SoftwareConfig): class CloudConfig(software_config.SoftwareConfig):
@ -54,8 +55,8 @@ class CloudConfig(software_config.SoftwareConfig):
cloud_config = yaml.dump(self.properties.get( cloud_config = yaml.dump(self.properties.get(
self.CLOUD_CONFIG), Dumper=yaml_dumper) self.CLOUD_CONFIG), Dumper=yaml_dumper)
props[self.CONFIG] = '#cloud-config\n%s' % cloud_config props[self.CONFIG] = '#cloud-config\n%s' % cloud_config
sc = self.heat().software_configs.create(**props) sc = self.rpc_client().create_software_config(self.context, **props)
self.resource_id_set(sc.id) self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID])
def resource_mapping(): def resource_mapping():

View File

@ -16,11 +16,13 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
import os import os
from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import constraints from heat.engine import constraints
from heat.engine import properties from heat.engine import properties
from heat.engine.resources.software_config import software_config from heat.engine.resources.software_config import software_config
from heat.engine import support from heat.engine import support
from heat.rpc import api as rpc_api
class MultipartMime(software_config.SoftwareConfig): class MultipartMime(software_config.SoftwareConfig):
@ -95,8 +97,8 @@ class MultipartMime(software_config.SoftwareConfig):
def handle_create(self): def handle_create(self):
props = {self.NAME: self.physical_resource_name()} props = {self.NAME: self.physical_resource_name()}
props[self.CONFIG] = self.get_message() props[self.CONFIG] = self.get_message()
sc = self.heat().software_configs.create(**props) sc = self.rpc_client().create_software_config(self.context, **props)
self.resource_id_set(sc.id) self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID])
def get_message(self): def get_message(self):
if self.message: if self.message:
@ -107,10 +109,14 @@ class MultipartMime(software_config.SoftwareConfig):
config = item.get(self.CONFIG) config = item.get(self.CONFIG)
part_type = item.get(self.TYPE, self.TEXT) part_type = item.get(self.TYPE, self.TEXT)
part = config part = config
try: try:
part = self.heat().software_configs.get(config).config sc = self.rpc_client().show_software_config(
except Exception as ex: self.context, self.resource_id)
self.client_plugin().ignore_not_found(ex) except exception.NotFound:
pass
else:
part = sc[rpc_api.SOFTWARE_CONFIG_CONFIG]
if part_type == self.MULTIPART: if part_type == self.MULTIPART:
self._append_multiparts(subparts, part) self._append_multiparts(subparts, part)

View File

@ -18,6 +18,7 @@ from heat.engine import properties
from heat.engine import resource from heat.engine import resource
from heat.engine.resources.software_config import software_config as sc from heat.engine.resources.software_config import software_config as sc
from heat.engine import support from heat.engine import support
from heat.rpc import api as rpc_api
class SoftwareComponent(sc.SoftwareConfig): class SoftwareComponent(sc.SoftwareConfig):
@ -116,8 +117,8 @@ class SoftwareComponent(sc.SoftwareConfig):
# set 'group' to enable component processing by in-instance hook # set 'group' to enable component processing by in-instance hook
props[self.GROUP] = 'component' props[self.GROUP] = 'component'
sc = self.heat().software_configs.create(**props) sc = self.rpc_client().create_software_config(self.context, **props)
self.resource_id_set(sc.id) self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID])
def _resolve_attribute(self, name): def _resolve_attribute(self, name):
''' '''
@ -129,14 +130,13 @@ class SoftwareComponent(sc.SoftwareConfig):
''' '''
if name == self.CONFIGS_ATTR and self.resource_id: if name == self.CONFIGS_ATTR and self.resource_id:
try: try:
config = self.heat().software_configs.get(self.resource_id).\ sc = self.rpc_client().show_software_config(
config self.context, self.resource_id)
# configs list is stored in 'config' property of parent class # configs list is stored in 'config' property of parent class
# (see handle_create) # (see handle_create)
return config.get(self.CONFIGS) return sc[rpc_api.SOFTWARE_CONFIG_CONFIG].get(self.CONFIGS)
except Exception as ex: except exception.NotFound:
if self.client_plugin().is_not_found(ex): return None
return None
def validate(self): def validate(self):
'''Validate SoftwareComponent properties consistency.''' '''Validate SoftwareComponent properties consistency.'''

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import attributes from heat.engine import attributes
from heat.engine import constraints from heat.engine import constraints
@ -18,6 +19,7 @@ from heat.engine import properties
from heat.engine import resource from heat.engine import resource
from heat.engine import support from heat.engine import support
from heat.openstack.common import log as logging from heat.openstack.common import log as logging
from heat.rpc import api as rpc_api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -152,14 +154,12 @@ class SoftwareConfig(resource.Resource):
), ),
} }
default_client_name = 'heat'
def handle_create(self): def handle_create(self):
props = dict(self.properties) props = dict(self.properties)
props[self.NAME] = self.physical_resource_name() props[self.NAME] = self.physical_resource_name()
sc = self.heat().software_configs.create(**props) sc = self.rpc_client().create_software_config(self.context, **props)
self.resource_id_set(sc.id) self.resource_id_set(sc[rpc_api.SOFTWARE_CONFIG_ID])
def handle_delete(self): def handle_delete(self):
@ -167,9 +167,10 @@ class SoftwareConfig(resource.Resource):
return return
try: try:
self.heat().software_configs.delete(self.resource_id) self.rpc_client().delete_software_config(
except Exception as ex: self.context, self.resource_id)
self.client_plugin().ignore_not_found(ex) except exception.NotFound:
pass
def _resolve_attribute(self, name): def _resolve_attribute(self, name):
''' '''
@ -178,11 +179,11 @@ class SoftwareConfig(resource.Resource):
''' '''
if name == self.CONFIG_ATTR and self.resource_id: if name == self.CONFIG_ATTR and self.resource_id:
try: try:
return self.heat().software_configs.get( sc = self.rpc_client().show_software_config(
self.resource_id).config self.context, self.resource_id)
except Exception as ex: return sc[rpc_api.SOFTWARE_CONFIG_CONFIG]
if self.client_plugin().is_not_found(ex): except exception.NotFound:
return None return None
def resource_mapping(): def resource_mapping():

View File

@ -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 signal_responder
from heat.engine import support from heat.engine import support
from heat.openstack.common import log as logging from heat.openstack.common import log as logging
from heat.rpc import api as rpc_api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -194,23 +195,26 @@ class SoftwareDeployment(signal_responder.SignalResponder):
def _delete_derived_config(self, derived_config_id): def _delete_derived_config(self, derived_config_id):
try: try:
self.heat().software_configs.delete(derived_config_id) self.rpc_client().delete_software_config(
except Exception as ex: self.context, derived_config_id)
self.client_plugin().ignore_not_found(ex) except exception.NotFound:
pass
def _get_derived_config(self, action, source_config): def _get_derived_config(self, action, source_config):
derived_params = self._build_derived_config_params( derived_params = self._build_derived_config_params(
action, source_config.to_dict()) action, source_config)
derived_config = self.heat().software_configs.create(**derived_params) derived_config = self.rpc_client().create_software_config(
return derived_config.id self.context, **derived_params)
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) 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]\ if action not in self.properties[self.DEPLOY_ACTIONS]\
and not config.group == 'component': and not config[rpc_api.SOFTWARE_CONFIG_GROUP] == 'component':
return return
props = self._build_properties( props = self._build_properties(
@ -430,7 +434,8 @@ class SoftwareDeployment(signal_responder.SignalResponder):
def handle_signal(self, details): def handle_signal(self, details):
sd = self.heat().software_deployments.get(self.resource_id) 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: if not sd.status == self.IN_PROGRESS:
# output values are only expected when in an IN_PROGRESS state # output values are only expected when in an IN_PROGRESS state
return return
@ -450,7 +455,7 @@ class SoftwareDeployment(signal_responder.SignalResponder):
else: else:
event_reason = 'deployment succeeded' event_reason = 'deployment succeeded'
for output in sc.outputs or []: for output in sc[rpc_api.SOFTWARE_CONFIG_OUTPUTS] or []:
out_key = output['name'] out_key = output['name']
if out_key in details: if out_key in details:
ov[out_key] = details[out_key] 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 # Since there is no value for this key yet, check the output schemas
# to find out if the key is valid # to find out if the key is valid
sc = self.heat().software_configs.get(self.properties[self.CONFIG]) sc = self.rpc_client().show_software_config(
output_keys = [output['name'] for output in sc.outputs] 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: if key not in output_keys and key not in self.ATTRIBUTES:
raise exception.InvalidTemplateAttribute(resource=self.name, raise exception.InvalidTemplateAttribute(resource=self.name,
key=key) key=key)

View File

@ -38,9 +38,8 @@ class CloudConfigTest(common.HeatTestCase):
'Properties': self.properties 'Properties': self.properties
}}})) }}}))
self.config = self.stack['config_mysql'] self.config = self.stack['config_mysql']
heat = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.config.heat = heat self.config._rpc_client = self.rpc_client
self.software_configs = heat.return_value.software_configs
def test_resource_mapping(self): def test_resource_mapping(self):
mapping = cc.resource_mapping() mapping = cc.resource_mapping()
@ -50,11 +49,10 @@ class CloudConfigTest(common.HeatTestCase):
self.assertIsInstance(self.config, cc.CloudConfig) self.assertIsInstance(self.config, cc.CloudConfig)
def test_handle_create(self): def test_handle_create(self):
sc = mock.MagicMock()
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
sc.id = config_id value = {'id': config_id}
self.software_configs.create.return_value = sc self.rpc_client.create_software_config.return_value = value
self.config.handle_create() self.config.handle_create()
self.assertEqual(config_id, self.config.resource_id) 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']) self.assertEqual('#cloud-config\n{foo: bar}\n', kwargs['config'])

View File

@ -13,9 +13,9 @@
import email import email
import heatclient.exc as exc
import mock import mock
from heat.common import exception as exc
from heat.engine import parser from heat.engine import parser
from heat.engine.resources.software_config import multi_part as mp from heat.engine.resources.software_config import multi_part as mp
from heat.engine import template from heat.engine import template
@ -43,9 +43,8 @@ class MultipartMimeTest(common.HeatTestCase):
'parts': parts 'parts': parts
}}}})) }}}}))
self.config = stack['config_mysql'] self.config = stack['config_mysql']
heat = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.config.heat = heat self.config._rpc_client = self.rpc_client
self.software_configs = heat.return_value.software_configs
def test_resource_mapping(self): def test_resource_mapping(self):
mapping = mp.resource_mapping() mapping = mp.resource_mapping()
@ -55,13 +54,12 @@ class MultipartMimeTest(common.HeatTestCase):
self.assertIsInstance(self.config, mp.MultipartMime) self.assertIsInstance(self.config, mp.MultipartMime)
def test_handle_create(self): def test_handle_create(self):
sc = mock.MagicMock()
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
sc.id = config_id sc = {'id': config_id}
self.software_configs.create.return_value = sc self.rpc_client.create_software_config.return_value = sc
self.config.handle_create() self.config.handle_create()
self.assertEqual(config_id, self.config.resource_id) 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']) self.assertEqual(self.config.message, args['config'])
def test_get_message_not_none(self): def test_get_message_not_none(self):
@ -82,7 +80,9 @@ class MultipartMimeTest(common.HeatTestCase):
'type': 'text' 'type': 'text'
}] }]
self.init_config(parts=parts) 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() result = self.config.get_message()
message = email.message_from_string(result) message = email.message_from_string(result)
self.assertTrue(message.is_multipart()) self.assertTrue(message.is_multipart())
@ -96,7 +96,7 @@ class MultipartMimeTest(common.HeatTestCase):
'type': 'text' 'type': 'text'
}] }]
self.init_config(parts=parts) 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() result = self.config.get_message()
message = email.message_from_string(result) message = email.message_from_string(result)
self.assertTrue(message.is_multipart()) self.assertTrue(message.is_multipart())
@ -111,7 +111,9 @@ class MultipartMimeTest(common.HeatTestCase):
'filename': '/opt/stack/configure.d/55-heat-config' 'filename': '/opt/stack/configure.d/55-heat-config'
}] }]
self.init_config(parts=parts) 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() result = self.config.get_message()
message = email.message_from_string(result) message = email.message_from_string(result)
self.assertTrue(message.is_multipart()) self.assertTrue(message.is_multipart())
@ -138,7 +140,11 @@ class MultipartMimeTest(common.HeatTestCase):
'type': 'multipart' 'type': 'multipart'
}] }]
self.init_config(parts=parts) 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() result = self.config.get_message()
message = email.message_from_string(result) message = email.message_from_string(result)
self.assertTrue(message.is_multipart()) self.assertTrue(message.is_multipart())
@ -155,7 +161,9 @@ class MultipartMimeTest(common.HeatTestCase):
{'config': '9cab10ef-16ce-4be9-8b25-a67b7313eddb', {'config': '9cab10ef-16ce-4be9-8b25-a67b7313eddb',
'type': 'text'}] 'type': 'text'}]
self.init_config(parts=parts) 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() result = self.config.get_message()
message = email.message_from_string(result) message = email.message_from_string(result)
self.assertTrue(message.is_multipart()) self.assertTrue(message.is_multipart())

View File

@ -26,7 +26,6 @@ from heat.common.i18n import _
from heat.common import template_format from heat.common import template_format
from heat.db import api as db_api from heat.db import api as db_api
from heat.engine.clients.os import glance 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 nova
from heat.engine.clients.os import swift from heat.engine.clients.os import swift
from heat.engine import environment from heat.engine import environment
@ -531,12 +530,11 @@ class ServersTest(common.HeatTestCase):
server = servers.Server('WebServer', server = servers.Server('WebServer',
resource_defns['WebServer'], stack) resource_defns['WebServer'], stack)
self.m.StubOutWithMock(heat_plugin.HeatClientPlugin, '_create') self.rpc_client = mock.MagicMock()
heat_client = mock.Mock() server._rpc_client = self.rpc_client
heat_plugin.HeatClientPlugin._create().AndReturn(heat_client)
sc = mock.Mock() sc = {'config': 'wordpress from config'}
sc.config = 'wordpress from config' self.rpc_client.show_software_config.return_value = sc
heat_client.software_configs.get.return_value = sc
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc) nova.NovaClientPlugin._create().AndReturn(self.fc)
@ -572,11 +570,10 @@ class ServersTest(common.HeatTestCase):
server = servers.Server('WebServer', server = servers.Server('WebServer',
resource_defns['WebServer'], stack) resource_defns['WebServer'], stack)
self.m.StubOutWithMock(heat_plugin.HeatClientPlugin, '_create') self.rpc_client = mock.MagicMock()
heat_client = mock.Mock() server._rpc_client = self.rpc_client
heat_plugin.HeatClientPlugin._create().AndReturn(heat_client)
heat_client.software_configs.get.side_effect = \ self.rpc_client.show_software_config.side_effect = exception.NotFound
heat_plugin.exc.HTTPNotFound()
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create') self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc) nova.NovaClientPlugin._create().AndReturn(self.fc)

View File

@ -14,14 +14,13 @@
import mock import mock
import six import six
from heat.common import exception from heat.common import exception as exc
from heat.common import template_format from heat.common import template_format
from heat.engine.resources.software_config import software_component as sc from heat.engine.resources.software_config import software_component as sc
from heat.engine import stack from heat.engine import stack
from heat.engine import template from heat.engine import template
from heat.tests import common from heat.tests import common
from heat.tests import utils from heat.tests import utils
from heatclient.exc import HTTPNotFound
class SoftwareComponentTest(common.HeatTestCase): class SoftwareComponentTest(common.HeatTestCase):
@ -58,11 +57,8 @@ class SoftwareComponentTest(common.HeatTestCase):
self.ctx, 'software_component_test_stack', self.ctx, 'software_component_test_stack',
template.Template(self.template)) template.Template(self.template))
self.component = self.stack['mysql_component'] self.component = self.stack['mysql_component']
heat = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.heatclient = mock.MagicMock() self.component._rpc_client = self.rpc_client
self.component.heat = heat
heat.return_value = self.heatclient
self.software_configs = self.heatclient.software_configs
def test_resource_mapping(self): def test_resource_mapping(self):
mapping = sc.resource_mapping() mapping = sc.resource_mapping()
@ -72,10 +68,9 @@ class SoftwareComponentTest(common.HeatTestCase):
self.assertIsInstance(self.component, sc.SoftwareComponent) self.assertIsInstance(self.component, sc.SoftwareComponent)
def test_handle_create(self): def test_handle_create(self):
value = mock.MagicMock()
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value.id = config_id value = {'id': config_id}
self.software_configs.create.return_value = value self.rpc_client.create_software_config.return_value = value
self.component.handle_create() self.component.handle_create()
self.assertEqual(config_id, self.component.resource_id) self.assertEqual(config_id, self.component.resource_id)
@ -84,9 +79,9 @@ class SoftwareComponentTest(common.HeatTestCase):
self.assertIsNone(self.component.handle_delete()) self.assertIsNone(self.component.handle_delete())
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
self.component.resource_id = config_id 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.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()) self.assertIsNone(self.component.handle_delete())
def test_resolve_attribute(self): def test_resolve_attribute(self):
@ -94,14 +89,13 @@ class SoftwareComponentTest(common.HeatTestCase):
self.component.resource_id = None self.component.resource_id = None
self.assertIsNone(self.component._resolve_attribute('configs')) self.assertIsNone(self.component._resolve_attribute('configs'))
self.component.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' self.component.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value = mock.MagicMock()
configs = self.\ configs = self.\
template['resources']['mysql_component']['properties']['configs'] template['resources']['mysql_component']['properties']['configs']
# configs list is stored in 'config' property of SoftwareConfig # configs list is stored in 'config' property of SoftwareConfig
value.config = {'configs': configs} value = {'config': {'configs': configs}}
self.software_configs.get.return_value = value self.rpc_client.show_software_config.return_value = value
self.assertEqual(configs, self.component._resolve_attribute('configs')) 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')) self.assertIsNone(self.component._resolve_attribute('configs'))
@ -160,7 +154,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
echo CREATE $foo echo CREATE $foo
tool: script tool: script
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='Unknown Property config') err_msg='Unknown Property config')
), ),
( (
@ -172,7 +166,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
inputs: inputs:
- name: foo - name: foo
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='Property configs not assigned') err_msg='Property configs not assigned')
), ),
# do not test until bug #1350840 # do not test until bug #1350840
@ -199,7 +193,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
config: #!/bin/bash config: #!/bin/bash
tool: script tool: script
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='is not a list') err_msg='is not a list')
), ),
( (
@ -213,7 +207,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
config: #!/bin/bash config: #!/bin/bash
tool: script tool: script
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='actions length (0) is out of range ' err_msg='actions length (0) is out of range '
'(min: 1, max: None)') '(min: 1, max: None)')
), ),
@ -231,7 +225,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
config: #!/bin/bash config: #!/bin/bash
tool: script tool: script
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='Defining more than one configuration for the same ' err_msg='Defining more than one configuration for the same '
'action in SoftwareComponent "component" is not ' 'action in SoftwareComponent "component" is not '
'allowed.') 'allowed.')
@ -250,7 +244,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
config: #!/bin/bash config: #!/bin/bash
tool: script tool: script
''', ''',
err=exception.StackValidationFailed, err=exc.StackValidationFailed,
err_msg='Defining more than one configuration for the same ' err_msg='Defining more than one configuration for the same '
'action in SoftwareComponent "component" is not ' 'action in SoftwareComponent "component" is not '
'allowed.') 'allowed.')
@ -272,11 +266,7 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
self.ctx, 'software_component_test_stack', self.ctx, 'software_component_test_stack',
template.Template(self.template)) template.Template(self.template))
self.component = self.stack['component'] self.component = self.stack['component']
heat = mock.MagicMock() self.component._rpc_client = mock.MagicMock()
self.heatclient = mock.MagicMock()
self.component.heat = heat
heat.return_value = self.heatclient
self.software_configs = self.heatclient.software_configs
def test_properties_schema(self): def test_properties_schema(self):
if self.err: if self.err:

View File

@ -11,9 +11,9 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from heatclient.exc import HTTPNotFound
import mock import mock
from heat.common import exception as exc
from heat.engine import parser from heat.engine import parser
from heat.engine.resources.software_config import software_config as sc from heat.engine.resources.software_config import software_config as sc
from heat.engine import template from heat.engine import template
@ -43,11 +43,8 @@ class SoftwareConfigTest(common.HeatTestCase):
'Properties': self.properties 'Properties': self.properties
}}})) }}}))
self.config = self.stack['config_mysql'] self.config = self.stack['config_mysql']
heat = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.heatclient = mock.MagicMock() self.config._rpc_client = self.rpc_client
self.config.heat = heat
heat.return_value = self.heatclient
self.software_configs = self.heatclient.software_configs
def test_resource_mapping(self): def test_resource_mapping(self):
mapping = sc.resource_mapping() mapping = sc.resource_mapping()
@ -57,10 +54,9 @@ class SoftwareConfigTest(common.HeatTestCase):
self.assertIsInstance(self.config, sc.SoftwareConfig) self.assertIsInstance(self.config, sc.SoftwareConfig)
def test_handle_create(self): def test_handle_create(self):
value = mock.MagicMock()
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value.id = config_id value = {'id': config_id}
self.software_configs.create.return_value = value self.rpc_client.create_software_config.return_value = value
self.config.handle_create() self.config.handle_create()
self.assertEqual(config_id, self.config.resource_id) self.assertEqual(config_id, self.config.resource_id)
@ -69,9 +65,9 @@ class SoftwareConfigTest(common.HeatTestCase):
self.assertIsNone(self.config.handle_delete()) self.assertIsNone(self.config.handle_delete())
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
self.config.resource_id = config_id 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.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()) self.assertIsNone(self.config.handle_delete())
def test_resolve_attribute(self): def test_resolve_attribute(self):
@ -79,10 +75,9 @@ class SoftwareConfigTest(common.HeatTestCase):
self.config.resource_id = None self.config.resource_id = None
self.assertIsNone(self.config._resolve_attribute('config')) self.assertIsNone(self.config._resolve_attribute('config'))
self.config.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' self.config.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
value = mock.MagicMock() value = {'config': '#!/bin/bash'}
value.config = '#!/bin/bash' self.rpc_client.show_software_config.return_value = value
self.software_configs.get.return_value = value
self.assertEqual( self.assertEqual(
'#!/bin/bash', self.config._resolve_attribute('config')) '#!/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')) self.assertEqual(None, self.config._resolve_attribute('config'))

View File

@ -17,7 +17,7 @@ import copy
import mock import mock
import six import six
from heat.common import exception from heat.common import exception as exc
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import parser from heat.engine import parser
from heat.engine.resources.software_config import software_deployment as sd 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' get_signed_url.return_value = 'http://192.0.2.2/signed_url'
self.deployment = self.stack['deployment_mysql'] self.deployment = self.stack['deployment_mysql']
self.rpc_client = mock.MagicMock()
self.deployment._rpc_client = self.rpc_client
heat = mock.MagicMock() heat = mock.MagicMock()
self.deployment.heat = heat self.deployment.heat = heat
self.deployments = heat.return_value.software_deployments self.deployments = heat.return_value.software_deployments
self.software_configs = heat.return_value.software_configs
def test_validate(self): def test_validate(self):
template = dict(self.template_with_server) template = dict(self.template_with_server)
@ -138,7 +141,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
props['user_data_format'] = 'RAW' props['user_data_format'] = 'RAW'
self._create_stack(template) self._create_stack(template)
sd = self.deployment sd = self.deployment
err = self.assertRaises(exception.StackValidationFailed, sd.validate) err = self.assertRaises(exc.StackValidationFailed, sd.validate)
self.assertEqual("Resource server's property " self.assertEqual("Resource server's property "
"user_data_format should be set to " "user_data_format should be set to "
"SOFTWARE_CONFIG since there are " "SOFTWARE_CONFIG since there are "
@ -149,7 +152,6 @@ class SoftwareDeploymentTest(common.HeatTestCase):
self.assertIsInstance(self.deployment, sd.SoftwareDeployment) self.assertIsInstance(self.deployment, sd.SoftwareDeployment)
def mock_software_config(self): def mock_software_config(self):
sc = mock.MagicMock()
config = { config = {
'id': '48e8ade1-9196-42d5-89a2-f709fde42632', 'id': '48e8ade1-9196-42d5-89a2-f709fde42632',
'group': 'Test::Group', 'group': 'Test::Group',
@ -167,14 +169,10 @@ class SoftwareDeploymentTest(common.HeatTestCase):
}], }],
'outputs': [], 'outputs': [],
} }
sc.to_dict.return_value = config self.rpc_client.show_software_config.return_value = config
sc.group = 'Test::Group' return config
sc.config = config['config']
self.software_configs.get.return_value = sc
return sc
def mock_software_component(self): def mock_software_component(self):
sc = mock.MagicMock()
config = { config = {
'id': '48e8ade1-9196-42d5-89a2-f709fde42632', 'id': '48e8ade1-9196-42d5-89a2-f709fde42632',
'group': 'component', 'group': 'component',
@ -220,16 +218,12 @@ class SoftwareDeploymentTest(common.HeatTestCase):
}], }],
'outputs': [], 'outputs': [],
} }
sc.to_dict.return_value = config self.rpc_client.show_software_config.return_value = config
sc.group = 'component' return config
sc.config = config['config']
self.software_configs.get.return_value = sc
return sc
def mock_derived_software_config(self): def mock_derived_software_config(self):
sc = mock.MagicMock() sc = {'id': '9966c8e7-bc9c-42de-aa7d-f2447a952cb2'}
sc.id = '9966c8e7-bc9c-42de-aa7d-f2447a952cb2' self.rpc_client.create_software_config.return_value = sc
self.software_configs.create.return_value = sc
return sc return sc
def mock_deployment(self): def mock_deployment(self):
@ -290,11 +284,11 @@ class SoftwareDeploymentTest(common.HeatTestCase):
}], }],
'options': {}, 'options': {},
'outputs': [] 'outputs': []
}, self.software_configs.create.call_args[1]) }, self.rpc_client.create_software_config.call_args[1])
self.assertEqual( self.assertEqual(
{'action': 'CREATE', {'action': 'CREATE',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'COMPLETE', 'status': 'COMPLETE',
@ -381,11 +375,11 @@ class SoftwareDeploymentTest(common.HeatTestCase):
}], }],
'options': {}, 'options': {},
'outputs': [] 'outputs': []
}, self.software_configs.create.call_args[1]) }, self.rpc_client.create_software_config.call_args[1])
self.assertEqual( self.assertEqual(
{'action': 'CREATE', {'action': 'CREATE',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'COMPLETE', 'status': 'COMPLETE',
@ -404,7 +398,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
args = self.deployments.create.call_args[1] args = self.deployments.create.call_args[1]
self.assertEqual( self.assertEqual(
{'action': 'CREATE', {'action': 'CREATE',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'IN_PROGRESS', 'status': 'IN_PROGRESS',
@ -449,7 +443,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
sd.status = self.deployment.FAILED sd.status = self.deployment.FAILED
sd.status_reason = 'something wrong' sd.status_reason = 'something wrong'
err = self.assertRaises( err = self.assertRaises(
exception.Error, self.deployment.check_create_complete, sd) exc.Error, self.deployment.check_create_complete, sd)
self.assertEqual( self.assertEqual(
'Deployment to server failed: something wrong', six.text_type(err)) 'Deployment to server failed: something wrong', six.text_type(err))
@ -479,7 +473,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
args = sd.update.call_args[1] args = sd.update.call_args[1]
self.assertEqual({ self.assertEqual({
'action': 'DELETE', 'action': 'DELETE',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'IN_PROGRESS', 'status': 'IN_PROGRESS',
@ -499,14 +493,15 @@ class SoftwareDeploymentTest(common.HeatTestCase):
self.mock_software_config() self.mock_software_config()
derived_sc = self.mock_derived_software_config() derived_sc = self.mock_derived_software_config()
sd = self.mock_deployment() sd = self.mock_deployment()
sd.config_id = derived_sc.id sd.config_id = derived_sc['id']
self.deployments.get.return_value = sd self.deployments.get.return_value = sd
sd.delete.side_effect = HTTPNotFound() 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.assertIsNone(self.deployment.handle_delete())
self.assertEqual( 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): def test_handle_delete_none(self):
self._create_stack(self.template) self._create_stack(self.template)
@ -539,13 +534,14 @@ class SoftwareDeploymentTest(common.HeatTestCase):
self.deployment.handle_update( self.deployment.handle_update(
json_snippet=snippet, tmpl_diff=None, prop_diff=prop_diff) json_snippet=snippet, tmpl_diff=None, prop_diff=prop_diff)
self.assertEqual( 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] args = self.deployments.get.call_args[0]
self.assertEqual(1, len(args)) self.assertEqual(1, len(args))
self.assertIn(sd.id, args) self.assertIn(sd.id, args)
args = sd.update.call_args[1] 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): def test_handle_suspend_resume(self):
self._create_stack(self.template_delete_suspend_resume) self._create_stack(self.template_delete_suspend_resume)
@ -564,7 +560,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
args = sd.update.call_args[1] args = sd.update.call_args[1]
self.assertEqual({ self.assertEqual({
'action': 'SUSPEND', 'action': 'SUSPEND',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'IN_PROGRESS', 'status': 'IN_PROGRESS',
@ -583,7 +579,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
args = sd.update.call_args[1] args = sd.update.call_args[1]
self.assertEqual({ self.assertEqual({
'action': 'RESUME', 'action': 'RESUME',
'config_id': derived_sc.id, 'config_id': derived_sc['id'],
'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', 'server_id': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0',
'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591', 'stack_user_project_id': '65728b74-cfe7-4f17-9c15-11d4f686e591',
'status': 'IN_PROGRESS', 'status': 'IN_PROGRESS',
@ -599,16 +595,18 @@ class SoftwareDeploymentTest(common.HeatTestCase):
def test_handle_signal_ok_zero(self): def test_handle_signal_ok_zero(self):
self._create_stack(self.template) self._create_stack(self.template)
sd = mock.MagicMock() sd = mock.MagicMock()
sc = mock.MagicMock() sc = {
sc.outputs = [{'name': 'foo'}, 'outputs': [
{'name': 'foo2'}, {'name': 'foo'},
{'name': 'failed', {'name': 'foo2'},
'error_output': True}] {'name': 'failed', 'error_output': True}
]
}
sd.output_values = {} sd.output_values = {}
sd.status = self.deployment.IN_PROGRESS sd.status = self.deployment.IN_PROGRESS
sd.update.return_value = None sd.update.return_value = None
self.deployments.get.return_value = sd self.deployments.get.return_value = sd
self.software_configs.get.return_value = sc self.rpc_client.show_software_config.return_value = sc
details = { details = {
'foo': 'bar', 'foo': 'bar',
'deploy_status_code': 0 'deploy_status_code': 0
@ -630,16 +628,18 @@ class SoftwareDeploymentTest(common.HeatTestCase):
def test_handle_signal_ok_str_zero(self): def test_handle_signal_ok_str_zero(self):
self._create_stack(self.template) self._create_stack(self.template)
sd = mock.MagicMock() sd = mock.MagicMock()
sc = mock.MagicMock() sc = {
sc.outputs = [{'name': 'foo'}, 'outputs': [
{'name': 'foo2'}, {'name': 'foo'},
{'name': 'failed', {'name': 'foo2'},
'error_output': True}] {'name': 'failed', 'error_output': True}
]
}
sd.output_values = {} sd.output_values = {}
sd.status = self.deployment.IN_PROGRESS sd.status = self.deployment.IN_PROGRESS
sd.update.return_value = None sd.update.return_value = None
self.deployments.get.return_value = sd self.deployments.get.return_value = sd
self.software_configs.get.return_value = sc self.rpc_client.show_software_config.return_value = sc
details = { details = {
'foo': 'bar', 'foo': 'bar',
'deploy_status_code': '0' 'deploy_status_code': '0'
@ -661,16 +661,18 @@ class SoftwareDeploymentTest(common.HeatTestCase):
def test_handle_signal_failed(self): def test_handle_signal_failed(self):
self._create_stack(self.template) self._create_stack(self.template)
sd = mock.MagicMock() sd = mock.MagicMock()
sc = mock.MagicMock() sc = {
sc.outputs = [{'name': 'foo'}, 'outputs': [
{'name': 'foo2'}, {'name': 'foo'},
{'name': 'failed', {'name': 'foo2'},
'error_output': True}] {'name': 'failed', 'error_output': True}
]
}
sd.output_values = {} sd.output_values = {}
sd.status = self.deployment.IN_PROGRESS sd.status = self.deployment.IN_PROGRESS
sd.update.return_value = None sd.update.return_value = None
self.deployments.get.return_value = sd 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.'} details = {'failed': 'no enough memory found.'}
ret = self.deployment.handle_signal(details) ret = self.deployment.handle_signal(details)
self.assertEqual('deployment failed', ret) self.assertEqual('deployment failed', ret)
@ -760,7 +762,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
sd.outputs = [] sd.outputs = []
sd.output_values = {'foo': 'bar'} sd.output_values = {'foo': 'bar'}
err = self.assertRaises( err = self.assertRaises(
exception.InvalidTemplateAttribute, exc.InvalidTemplateAttribute,
self.deployment.FnGetAtt, 'foo2') self.deployment.FnGetAtt, 'foo2')
self.assertEqual( self.assertEqual(
'The Referenced Attribute (deployment_mysql foo2) is incorrect.', 'The Referenced Attribute (deployment_mysql foo2) is incorrect.',

View File

@ -44,9 +44,8 @@ class StructuredConfigTestJSON(common.HeatTestCase):
self.ctx, 'software_config_test_stack', self.ctx, 'software_config_test_stack',
template.Template(self.template)) template.Template(self.template))
self.config = self.stack['config_mysql'] self.config = self.stack['config_mysql']
heat = mock.MagicMock() self.rpc_client = mock.MagicMock()
self.config.heat = heat self.config._rpc_client = self.rpc_client
self.software_configs = heat.return_value.software_configs
def test_resource_mapping(self): def test_resource_mapping(self):
mapping = sc.resource_mapping() mapping = sc.resource_mapping()
@ -60,13 +59,12 @@ class StructuredConfigTestJSON(common.HeatTestCase):
self.assertIsInstance(self.config, sc.StructuredConfig) self.assertIsInstance(self.config, sc.StructuredConfig)
def test_handle_create(self): def test_handle_create(self):
stc = mock.MagicMock()
config_id = 'c8a19429-7fde-47ea-a42f-40045488226c' config_id = 'c8a19429-7fde-47ea-a42f-40045488226c'
stc.id = config_id value = {'id': config_id}
self.software_configs.create.return_value = stc self.rpc_client.create_software_config.return_value = value
self.config.handle_create() self.config.handle_create()
self.assertEqual(config_id, self.config.resource_id) 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']) self.assertEqual(self.stored_config, kwargs['config'])