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

View File

@ -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 {}

View File

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

View File

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

View File

@ -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.'''

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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