Split engine service test cases (3)

This patch separates out test cases related to software_config and
software_deployments into a standalone module. The patch also replaces a
few mox calls by mock calls.

Change-Id: Ie2ed122dda16e1cbf8036044d3476e2b328f8796
This commit is contained in:
tengqm 2015-05-06 03:38:33 -04:00
parent 4fcfdf1e71
commit 022f645ee2
2 changed files with 673 additions and 660 deletions

View File

@ -0,0 +1,673 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import uuid
import mock
from oslo_messaging.rpc import dispatcher
from oslo_serialization import jsonutils as json
from oslo_utils import timeutils
import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import swift
from heat.engine import service
from heat.engine import service_software_config
from heat.objects import resource as resource_objects
from heat.objects import software_deployment as software_deployment_object
from heat.tests import common
from heat.tests.engine import tools
from heat.tests import utils
class SoftwareConfigServiceTest(common.HeatTestCase):
def setUp(self):
super(SoftwareConfigServiceTest, self).setUp()
self.ctx = utils.dummy_context()
self.patch('heat.engine.service.warnings')
self.engine = service.EngineService('a-host', 'a-topic')
def _create_software_config(
self, group='Heat::Shell', name='config_mysql', config=None,
inputs=None, outputs=None, options=None):
inputs = inputs or []
outputs = outputs or []
options = options or {}
return self.engine.create_software_config(
self.ctx, group, name, config, inputs, outputs, options)
def test_show_software_config(self):
config_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_config,
self.ctx, config_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
config = self._create_software_config()
res = self.engine.show_software_config(self.ctx, config['id'])
self.assertEqual(config, res)
def test_create_software_config_new_ids(self):
config1 = self._create_software_config()
self.assertIsNotNone(config1)
config2 = self._create_software_config()
self.assertNotEqual(config1['id'], config2['id'])
def test_create_software_config(self):
kwargs = {
'group': 'Heat::Chef',
'name': 'config_heat',
'config': '...',
'inputs': [{'name': 'mode'}],
'outputs': [{'name': 'endpoint'}],
'options': {}
}
config = self._create_software_config(**kwargs)
config_id = config['id']
config = self.engine.show_software_config(self.ctx, config_id)
self.assertEqual(kwargs['group'], config['group'])
self.assertEqual(kwargs['name'], config['name'])
self.assertEqual(kwargs['config'], config['config'])
self.assertEqual(kwargs['inputs'], config['inputs'])
self.assertEqual(kwargs['outputs'], config['outputs'])
self.assertEqual(kwargs['options'], config['options'])
def test_delete_software_config(self):
config = self._create_software_config()
self.assertIsNotNone(config)
config_id = config['id']
self.engine.delete_software_config(self.ctx, config_id)
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_config,
self.ctx, config_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
def _create_software_deployment(self, config_id=None, input_values=None,
action='INIT',
status='COMPLETE', status_reason='',
config_group=None,
server_id=str(uuid.uuid4()),
config_name=None,
stack_user_project_id=None):
input_values = input_values or {}
if config_id is None:
config = self._create_software_config(group=config_group,
name=config_name)
config_id = config['id']
return self.engine.create_software_deployment(
self.ctx, server_id, config_id, input_values,
action, status, status_reason, stack_user_project_id)
def test_list_software_deployments(self):
stack_name = 'test_list_software_deployments'
t = template_format.parse(tools.wp_template)
stack = utils.parse_stack(t, stack_name=stack_name)
tools.setup_mocks(self.m, stack)
self.m.ReplayAll()
stack.store()
stack.create()
server = stack['WebServer']
server_id = server.resource_id
deployment = self._create_software_deployment(
server_id=server_id)
deployment_id = deployment['id']
self.assertIsNotNone(deployment)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
self.assertIsNotNone(deployments)
deployment_ids = [x['id'] for x in deployments]
self.assertIn(deployment_id, deployment_ids)
self.assertIn(deployment, deployments)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=str(uuid.uuid4()))
self.assertEqual([], deployments)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=server.resource_id)
self.assertEqual([deployment], deployments)
rs = resource_objects.Resource.get_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(deployment['config_id'],
rs.rsrc_metadata.get('deployments')[0]['id'])
def test_metadata_software_deployments(self):
stack_name = 'test_metadata_software_deployments'
t = template_format.parse(tools.wp_template)
stack = utils.parse_stack(t, stack_name=stack_name)
tools.setup_mocks(self.m, stack)
self.m.ReplayAll()
stack.store()
stack.create()
server = stack['WebServer']
server_id = server.resource_id
stack_user_project_id = str(uuid.uuid4())
d1 = self._create_software_deployment(
config_group='mygroup',
server_id=server_id,
config_name='02_second',
stack_user_project_id=stack_user_project_id)
d2 = self._create_software_deployment(
config_group='mygroup',
server_id=server_id,
config_name='01_first',
stack_user_project_id=stack_user_project_id)
d3 = self._create_software_deployment(
config_group='myothergroup',
server_id=server_id,
config_name='03_third',
stack_user_project_id=stack_user_project_id)
metadata = self.engine.metadata_software_deployments(
self.ctx, server_id=server_id)
self.assertEqual(3, len(metadata))
self.assertEqual('mygroup', metadata[1]['group'])
self.assertEqual('mygroup', metadata[0]['group'])
self.assertEqual('myothergroup', metadata[2]['group'])
self.assertEqual(d1['config_id'], metadata[1]['id'])
self.assertEqual(d2['config_id'], metadata[0]['id'])
self.assertEqual(d3['config_id'], metadata[2]['id'])
self.assertEqual('01_first', metadata[0]['name'])
self.assertEqual('02_second', metadata[1]['name'])
self.assertEqual('03_third', metadata[2]['name'])
# assert that metadata via metadata_software_deployments matches
# metadata via server resource
rs = resource_objects.Resource.get_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(metadata, rs.rsrc_metadata.get('deployments'))
deployments = self.engine.metadata_software_deployments(
self.ctx, server_id=str(uuid.uuid4()))
self.assertEqual([], deployments)
# assert get results when the context tenant_id matches
# the stored stack_user_project_id
ctx = utils.dummy_context(tenant_id=stack_user_project_id)
metadata = self.engine.metadata_software_deployments(
ctx, server_id=server_id)
self.assertEqual(3, len(metadata))
# assert get no results when the context tenant_id is unknown
ctx = utils.dummy_context(tenant_id=str(uuid.uuid4()))
metadata = self.engine.metadata_software_deployments(
ctx, server_id=server_id)
self.assertEqual(0, len(metadata))
def test_show_software_deployment(self):
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_deployment,
self.ctx, deployment_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
self.assertEqual(
deployment,
self.engine.show_software_deployment(self.ctx, deployment_id))
@mock.patch.object(service_software_config.SoftwareConfigService,
'_push_metadata_software_deployments')
def test_signal_software_deployment(self, pmsd):
self.assertRaises(ValueError,
self.engine.signal_software_deployment,
self.ctx, None, {}, None)
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.signal_software_deployment,
self.ctx, deployment_id, {}, None)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
deployment_id = deployment['id']
# signal is ignore unless deployment is IN_PROGRESS
self.assertIsNone(self.engine.signal_software_deployment(
self.ctx, deployment_id, {}, None))
# simple signal, no data
deployment = self._create_software_deployment(action='INIT',
status='IN_PROGRESS')
deployment_id = deployment['id']
res = self.engine.signal_software_deployment(
self.ctx, deployment_id, {}, None)
self.assertEqual('deployment succeeded', res)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('COMPLETE', sd.status)
self.assertEqual('Outputs received', sd.status_reason)
self.assertEqual({
'deploy_status_code': None,
'deploy_stderr': None,
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# simple signal, some data
config = self._create_software_config(outputs=[{'name': 'foo'}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{'foo': 'bar', 'deploy_status_code': 0},
None)
self.assertEqual('deployment succeeded', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('COMPLETE', sd.status)
self.assertEqual('Outputs received', sd.status_reason)
self.assertEqual({
'deploy_status_code': 0,
'foo': 'bar',
'deploy_stderr': None,
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# failed signal on deploy_status_code
config = self._create_software_config(outputs=[{'name': 'foo'}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{
'foo': 'bar',
'deploy_status_code': -1,
'deploy_stderr': 'Its gone Pete Tong'
},
None)
self.assertEqual('deployment failed (-1)', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status)
self.assertEqual(
('deploy_status_code : Deployment exited with non-zero '
'status code: -1'),
sd.status_reason)
self.assertEqual({
'deploy_status_code': -1,
'foo': 'bar',
'deploy_stderr': 'Its gone Pete Tong',
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# failed signal on error_output foo
config = self._create_software_config(outputs=[
{'name': 'foo', 'error_output': True}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{
'foo': 'bar',
'deploy_status_code': -1,
'deploy_stderr': 'Its gone Pete Tong'
},
None)
self.assertEqual('deployment failed', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status)
self.assertEqual(
('foo : bar, deploy_status_code : Deployment exited with '
'non-zero status code: -1'),
sd.status_reason)
self.assertEqual({
'deploy_status_code': -1,
'foo': 'bar',
'deploy_stderr': 'Its gone Pete Tong',
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
def test_create_software_deployment(self):
kwargs = {
'group': 'Heat::Chef',
'name': 'config_heat',
'config': '...',
'inputs': [{'name': 'mode'}],
'outputs': [{'name': 'endpoint'}],
'options': {}
}
config = self._create_software_config(**kwargs)
config_id = config['id']
kwargs = {
'config_id': config_id,
'input_values': {'mode': 'standalone'},
'action': 'INIT',
'status': 'COMPLETE',
'status_reason': ''
}
deployment = self._create_software_deployment(**kwargs)
deployment_id = deployment['id']
deployment = self.engine.show_software_deployment(
self.ctx, deployment_id)
self.assertEqual(deployment_id, deployment['id'])
self.assertEqual(kwargs['input_values'], deployment['input_values'])
@mock.patch.object(service_software_config.SoftwareConfigService,
'_refresh_software_deployment')
def test_show_software_deployment_refresh(
self, _refresh_software_deployment):
temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c'
'?temp_url_sig=ctemp_url_expires=1234')
config = self._create_software_config(inputs=[
{
'name': 'deploy_signal_transport',
'type': 'String',
'value': 'TEMP_URL_SIGNAL'
}, {
'name': 'deploy_signal_id',
'type': 'String',
'value': temp_url
}
])
deployment = self._create_software_deployment(
status='IN_PROGRESS', config_id=config['id'])
deployment_id = deployment['id']
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
_refresh_software_deployment.return_value = sd
self.assertEqual(
deployment,
self.engine.show_software_deployment(self.ctx, deployment_id))
self.assertEqual(
(self.ctx, sd, temp_url),
_refresh_software_deployment.call_args[0])
def test_update_software_deployment_new_config(self):
server_id = str(uuid.uuid4())
mock_push = self.patchobject(self.engine.software_config,
'_push_metadata_software_deployments')
deployment = self._create_software_deployment(server_id=server_id)
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployment_action = deployment['action']
self.assertEqual('INIT', deployment_action)
config_id = deployment['config_id']
self.assertIsNotNone(config_id)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id=deployment_id, config_id=config_id,
input_values={}, output_values={}, action='DEPLOY',
status='WAITING', status_reason='', updated_at=None)
self.assertIsNotNone(updated)
self.assertEqual(config_id, updated['config_id'])
self.assertEqual('DEPLOY', updated['action'])
self.assertEqual('WAITING', updated['status'])
self.assertEqual(2, mock_push.call_count)
def test_update_software_deployment_status(self):
server_id = str(uuid.uuid4())
mock_push = self.patchobject(self.engine.software_config,
'_push_metadata_software_deployments')
deployment = self._create_software_deployment(server_id=server_id)
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployment_action = deployment['action']
self.assertEqual('INIT', deployment_action)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id=deployment_id, config_id=None,
input_values=None, output_values={}, action='DEPLOY',
status='WAITING', status_reason='', updated_at=None)
self.assertIsNotNone(updated)
self.assertEqual('DEPLOY', updated['action'])
self.assertEqual('WAITING', updated['status'])
mock_push.assert_called_once_with(self.ctx, server_id)
def test_update_software_deployment_fields(self):
deployment = self._create_software_deployment()
deployment_id = deployment['id']
config_id = deployment['config_id']
def check_software_deployment_updated(**kwargs):
values = {
'config_id': None,
'input_values': {},
'output_values': {},
'action': {},
'status': 'WAITING',
'status_reason': ''
}
values.update(kwargs)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id, updated_at=None, **values)
for key, value in six.iteritems(kwargs):
self.assertEqual(value, updated[key])
check_software_deployment_updated(config_id=config_id)
check_software_deployment_updated(input_values={'foo': 'fooooo'})
check_software_deployment_updated(output_values={'bar': 'baaaaa'})
check_software_deployment_updated(action='DEPLOY')
check_software_deployment_updated(status='COMPLETE')
check_software_deployment_updated(status_reason='Done!')
def test_delete_software_deployment(self):
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.delete_software_deployment,
self.ctx, deployment_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
deployment_ids = [x['id'] for x in deployments]
self.assertIn(deployment_id, deployment_ids)
self.engine.delete_software_deployment(self.ctx, deployment_id)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
deployment_ids = [x['id'] for x in deployments]
self.assertNotIn(deployment_id, deployment_ids)
@mock.patch.object(service_software_config.SoftwareConfigService,
'metadata_software_deployments')
@mock.patch.object(service_software_config.resource_object.Resource,
'get_by_physical_resource_id')
@mock.patch.object(service_software_config.requests, 'put')
def test_push_metadata_software_deployments(self, put, res_get, md_sd):
rs = mock.Mock()
rs.rsrc_metadata = {'original': 'metadata'}
rs.data = []
res_get.return_value = rs
deployments = {'deploy': 'this'}
md_sd.return_value = deployments
result_metadata = {
'original': 'metadata',
'deployments': {'deploy': 'this'}
}
self.engine.software_config._push_metadata_software_deployments(
self.ctx, '1234')
rs.update_and_save.assert_called_once_with(
{'rsrc_metadata': result_metadata})
put.side_effect = Exception('Unexpected requests.put')
@mock.patch.object(service_software_config.SoftwareConfigService,
'metadata_software_deployments')
@mock.patch.object(service_software_config.resource_object.Resource,
'get_by_physical_resource_id')
@mock.patch.object(service_software_config.requests, 'put')
def test_push_metadata_software_deployments_temp_url(
self, put, res_get, md_sd):
rs = mock.Mock()
rs.rsrc_metadata = {'original': 'metadata'}
rd = mock.Mock()
rd.key = 'metadata_put_url'
rd.value = 'http://192.168.2.2/foo/bar'
rs.data = [rd]
res_get.return_value = rs
deployments = {'deploy': 'this'}
md_sd.return_value = deployments
result_metadata = {
'original': 'metadata',
'deployments': {'deploy': 'this'}
}
self.engine.software_config._push_metadata_software_deployments(
self.ctx, '1234')
rs.update_and_save.assert_called_once_with(
{'rsrc_metadata': result_metadata})
put.assert_called_once_with(
'http://192.168.2.2/foo/bar', json.dumps(result_metadata))
@mock.patch.object(service_software_config.SoftwareConfigService,
'signal_software_deployment')
@mock.patch.object(swift.SwiftClientPlugin, '_create')
def test_refresh_software_deployment(self, scc, ssd):
temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c'
'?temp_url_sig=ctemp_url_expires=1234')
container = 'b'
object_name = 'c'
config = self._create_software_config(inputs=[
{
'name': 'deploy_signal_transport',
'type': 'String',
'value': 'TEMP_URL_SIGNAL'
}, {
'name': 'deploy_signal_id',
'type': 'String',
'value': temp_url
}
])
timeutils.set_time_override(
datetime.datetime(2013, 1, 23, 22, 48, 5, 0))
self.addCleanup(timeutils.clear_time_override)
now = timeutils.utcnow()
then = now - datetime.timedelta(0, 60)
last_modified_1 = 'Wed, 23 Jan 2013 22:47:05 GMT'
last_modified_2 = 'Wed, 23 Jan 2013 22:48:05 GMT'
sc = mock.MagicMock()
headers = {
'last-modified': last_modified_1
}
sc.head_object.return_value = headers
sc.get_object.return_value = (headers, '{"foo": "bar"}')
scc.return_value = sc
deployment = self._create_software_deployment(
status='IN_PROGRESS', config_id=config['id'])
deployment_id = six.text_type(deployment['id'])
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
# poll with missing object
swift_exc = swift.SwiftClientPlugin.exceptions_module
sc.head_object.side_effect = swift_exc.ClientException(
'Not found', http_status=404)
self.assertEqual(
sd,
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url))
sc.head_object.assert_called_once_with(container, object_name)
# no call to get_object or signal_last_modified
self.assertEqual([], sc.get_object.mock_calls)
self.assertEqual([], ssd.mock_calls)
# poll with other error
sc.head_object.side_effect = swift_exc.ClientException(
'Ouch', http_status=409)
self.assertRaises(
swift_exc.ClientException,
self.engine.software_config._refresh_software_deployment,
self.ctx,
sd,
temp_url)
# no call to get_object or signal_last_modified
self.assertEqual([], sc.get_object.mock_calls)
self.assertEqual([], ssd.mock_calls)
sc.head_object.side_effect = None
# first poll populates data signal_last_modified
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
sc.head_object.assert_called_with(container, object_name)
sc.get_object.assert_called_once_with(container, object_name)
# signal_software_deployment called with signal
ssd.assert_called_once_with(self.ctx, deployment_id, {u"foo": u"bar"},
timeutils.strtime(then))
# second poll updated_at populated with first poll last-modified
software_deployment_object.SoftwareDeployment.update_by_id(
self.ctx, deployment_id, {'updated_at': then})
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual(then, sd.updated_at)
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
sc.get_object.assert_called_once_with(container, object_name)
# signal_software_deployment has not been called again
ssd.assert_called_once_with(self.ctx, deployment_id, {"foo": "bar"},
timeutils.strtime(then))
# third poll last-modified changed, new signal
headers['last-modified'] = last_modified_2
sc.head_object.return_value = headers
sc.get_object.return_value = (headers, '{"bar": "baz"}')
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
# two calls to signal_software_deployment, for then and now
self.assertEqual(2, len(ssd.mock_calls))
ssd.assert_called_with(self.ctx, deployment_id, {"bar": "baz"},
timeutils.strtime(now))
# four polls result in only two signals, for then and now
software_deployment_object.SoftwareDeployment.update_by_id(
self.ctx, deployment_id, {'updated_at': now})
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
self.assertEqual(2, len(ssd.mock_calls))

View File

@ -21,7 +21,6 @@ import mox
from oslo_config import cfg
from oslo_messaging.rpc import dispatcher
from oslo_serialization import jsonutils as json
from oslo_utils import timeutils
import six
from heat.common import context
@ -30,14 +29,12 @@ from heat.common import identifier
from heat.common import messaging
from heat.common import service_utils
from heat.common import template_format
from heat.engine.clients.os import swift
from heat.engine import dependencies
from heat.engine import environment
from heat.engine import properties
from heat.engine import resource as res
from heat.engine.resources.aws.ec2 import instance as instances
from heat.engine import service
from heat.engine import service_software_config
from heat.engine import service_stack_watch
from heat.engine import stack as parser
from heat.engine import stack_lock
@ -47,7 +44,6 @@ from heat.engine import worker
from heat.objects import event as event_object
from heat.objects import resource as resource_objects
from heat.objects import service as service_objects
from heat.objects import software_deployment as software_deployment_object
from heat.objects import stack as stack_object
from heat.objects import stack_lock as stack_lock_object
from heat.objects import sync_point as sync_point_object
@ -3853,662 +3849,6 @@ class StackServiceTest(common.HeatTestCase):
)
class SoftwareConfigServiceTest(common.HeatTestCase):
def setUp(self):
super(SoftwareConfigServiceTest, self).setUp()
self.ctx = utils.dummy_context()
self.patch('heat.engine.service.warnings')
self.engine = service.EngineService('a-host', 'a-topic')
def _create_software_config(
self, group='Heat::Shell', name='config_mysql', config=None,
inputs=None, outputs=None, options=None):
inputs = inputs or []
outputs = outputs or []
options = options or {}
return self.engine.create_software_config(
self.ctx, group, name, config, inputs, outputs, options)
def test_show_software_config(self):
config_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_config,
self.ctx, config_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
config = self._create_software_config()
config_id = config['id']
self.assertEqual(
config, self.engine.show_software_config(self.ctx, config_id))
def test_create_software_config(self):
config = self._create_software_config()
self.assertIsNotNone(config)
config_id = config['id']
config = self._create_software_config()
self.assertNotEqual(config_id, config['id'])
kwargs = {
'group': 'Heat::Chef',
'name': 'config_heat',
'config': '...',
'inputs': [{'name': 'mode'}],
'outputs': [{'name': 'endpoint'}],
'options': {}
}
config = self._create_software_config(**kwargs)
config_id = config['id']
config = self.engine.show_software_config(self.ctx, config_id)
self.assertEqual(kwargs['group'], config['group'])
self.assertEqual(kwargs['name'], config['name'])
self.assertEqual(kwargs['config'], config['config'])
self.assertEqual(kwargs['inputs'], config['inputs'])
self.assertEqual(kwargs['outputs'], config['outputs'])
self.assertEqual(kwargs['options'], config['options'])
def test_delete_software_config(self):
config = self._create_software_config()
self.assertIsNotNone(config)
config_id = config['id']
self.engine.delete_software_config(self.ctx, config_id)
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_config,
self.ctx, config_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
def _create_software_deployment(self, config_id=None, input_values=None,
action='INIT',
status='COMPLETE', status_reason='',
config_group=None,
server_id=str(uuid.uuid4()),
config_name=None,
stack_user_project_id=None):
input_values = input_values or {}
if config_id is None:
config = self._create_software_config(group=config_group,
name=config_name)
config_id = config['id']
return self.engine.create_software_deployment(
self.ctx, server_id, config_id, input_values,
action, status, status_reason, stack_user_project_id)
def test_list_software_deployments(self):
stack_name = 'test_list_software_deployments'
stack = tools.get_stack(stack_name, self.ctx)
tools.setup_mocks(self.m, stack)
self.m.ReplayAll()
stack.store()
stack.create()
server = stack['WebServer']
server_id = server.resource_id
deployment = self._create_software_deployment(
server_id=server_id)
deployment_id = deployment['id']
self.assertIsNotNone(deployment)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
self.assertIsNotNone(deployments)
deployment_ids = [x['id'] for x in deployments]
self.assertIn(deployment_id, deployment_ids)
self.assertIn(deployment, deployments)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=str(uuid.uuid4()))
self.assertEqual([], deployments)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=server.resource_id)
self.assertEqual([deployment], deployments)
rs = resource_objects.Resource.get_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(deployment['config_id'],
rs.rsrc_metadata.get('deployments')[0]['id'])
def test_metadata_software_deployments(self):
stack_name = 'test_list_software_deployments'
stack = tools.get_stack(stack_name, self.ctx)
tools.setup_mocks(self.m, stack)
self.m.ReplayAll()
stack.store()
stack.create()
server = stack['WebServer']
server_id = server.resource_id
stack_user_project_id = str(uuid.uuid4())
d1 = self._create_software_deployment(
config_group='mygroup',
server_id=server_id,
config_name='02_second',
stack_user_project_id=stack_user_project_id)
d2 = self._create_software_deployment(
config_group='mygroup',
server_id=server_id,
config_name='01_first',
stack_user_project_id=stack_user_project_id)
d3 = self._create_software_deployment(
config_group='myothergroup',
server_id=server_id,
config_name='03_third',
stack_user_project_id=stack_user_project_id)
metadata = self.engine.metadata_software_deployments(
self.ctx, server_id=server_id)
self.assertEqual(3, len(metadata))
self.assertEqual('mygroup', metadata[1]['group'])
self.assertEqual('mygroup', metadata[0]['group'])
self.assertEqual('myothergroup', metadata[2]['group'])
self.assertEqual(d1['config_id'], metadata[1]['id'])
self.assertEqual(d2['config_id'], metadata[0]['id'])
self.assertEqual(d3['config_id'], metadata[2]['id'])
self.assertEqual('01_first', metadata[0]['name'])
self.assertEqual('02_second', metadata[1]['name'])
self.assertEqual('03_third', metadata[2]['name'])
# assert that metadata via metadata_software_deployments matches
# metadata via server resource
rs = resource_objects.Resource.get_by_physical_resource_id(
self.ctx, server_id)
self.assertEqual(metadata,
rs.rsrc_metadata.get('deployments'))
deployments = self.engine.metadata_software_deployments(
self.ctx, server_id=str(uuid.uuid4()))
self.assertEqual([], deployments)
# assert get results when the context tenant_id matches
# the stored stack_user_project_id
ctx = utils.dummy_context(tenant_id=stack_user_project_id)
metadata = self.engine.metadata_software_deployments(
ctx, server_id=server_id)
self.assertEqual(3, len(metadata))
# assert get no results when the context tenant_id is unknown
ctx = utils.dummy_context(tenant_id=str(uuid.uuid4()))
metadata = self.engine.metadata_software_deployments(
ctx, server_id=server_id)
self.assertEqual(0, len(metadata))
def test_show_software_deployment(self):
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.show_software_deployment,
self.ctx, deployment_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
self.assertEqual(
deployment,
self.engine.show_software_deployment(self.ctx, deployment_id))
@mock.patch.object(service_software_config.SoftwareConfigService,
'_push_metadata_software_deployments')
def test_signal_software_deployment(self, pmsd):
self.assertRaises(ValueError,
self.engine.signal_software_deployment,
self.ctx, None, {}, None)
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.signal_software_deployment,
self.ctx, deployment_id, {}, None)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
deployment_id = deployment['id']
# signal is ignore unless deployment is IN_PROGRESS
self.assertIsNone(self.engine.signal_software_deployment(
self.ctx, deployment_id, {}, None))
# simple signal, no data
deployment = self._create_software_deployment(
action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
self.assertEqual(
'deployment succeeded',
self.engine.signal_software_deployment(
self.ctx, deployment_id, {}, None))
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('COMPLETE', sd.status)
self.assertEqual('Outputs received', sd.status_reason)
self.assertEqual({
'deploy_status_code': None,
'deploy_stderr': None,
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# simple signal, some data
config = self._create_software_config(outputs=[{'name': 'foo'}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{'foo': 'bar', 'deploy_status_code': 0},
None)
self.assertEqual('deployment succeeded', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('COMPLETE', sd.status)
self.assertEqual('Outputs received', sd.status_reason)
self.assertEqual({
'deploy_status_code': 0,
'foo': 'bar',
'deploy_stderr': None,
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# failed signal on deploy_status_code
config = self._create_software_config(outputs=[
{'name': 'foo'}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{
'foo': 'bar',
'deploy_status_code': -1,
'deploy_stderr': 'Its gone Pete Tong'
},
None)
self.assertEqual('deployment failed (-1)', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status)
self.assertEqual(
('deploy_status_code : Deployment exited with non-zero '
'status code: -1'),
sd.status_reason)
self.assertEqual({
'deploy_status_code': -1,
'foo': 'bar',
'deploy_stderr': 'Its gone Pete Tong',
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
# failed signal on error_output foo
config = self._create_software_config(outputs=[
{'name': 'foo', 'error_output': True}])
deployment = self._create_software_deployment(
config_id=config['id'], action='INIT', status='IN_PROGRESS')
deployment_id = deployment['id']
result = self.engine.signal_software_deployment(
self.ctx,
deployment_id,
{
'foo': 'bar',
'deploy_status_code': -1,
'deploy_stderr': 'Its gone Pete Tong'
},
None)
self.assertEqual('deployment failed', result)
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status)
self.assertEqual(
('foo : bar, deploy_status_code : Deployment exited with '
'non-zero status code: -1'),
sd.status_reason)
self.assertEqual({
'deploy_status_code': -1,
'foo': 'bar',
'deploy_stderr': 'Its gone Pete Tong',
'deploy_stdout': None
}, sd.output_values)
self.assertIsNotNone(sd.updated_at)
def test_create_software_deployment(self):
kwargs = {
'group': 'Heat::Chef',
'name': 'config_heat',
'config': '...',
'inputs': [{'name': 'mode'}],
'outputs': [{'name': 'endpoint'}],
'options': {}
}
config = self._create_software_config(**kwargs)
config_id = config['id']
kwargs = {
'config_id': config_id,
'input_values': {'mode': 'standalone'},
'action': 'INIT',
'status': 'COMPLETE',
'status_reason': ''
}
deployment = self._create_software_deployment(**kwargs)
deployment_id = deployment['id']
deployment = self.engine.show_software_deployment(
self.ctx, deployment_id)
self.assertEqual(deployment_id, deployment['id'])
self.assertEqual(kwargs['input_values'], deployment['input_values'])
@mock.patch.object(service_software_config.SoftwareConfigService,
'_refresh_software_deployment')
def test_show_software_deployment_refresh(
self, _refresh_software_deployment):
temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c'
'?temp_url_sig=ctemp_url_expires=1234')
config = self._create_software_config(inputs=[
{
'name': 'deploy_signal_transport',
'type': 'String',
'value': 'TEMP_URL_SIGNAL'
}, {
'name': 'deploy_signal_id',
'type': 'String',
'value': temp_url
}
])
deployment = self._create_software_deployment(
status='IN_PROGRESS', config_id=config['id'])
deployment_id = deployment['id']
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
_refresh_software_deployment.return_value = sd
self.assertEqual(
deployment,
self.engine.show_software_deployment(self.ctx, deployment_id))
self.assertEqual(
(self.ctx, sd, temp_url),
_refresh_software_deployment.call_args[0])
def test_update_software_deployment_new_config(self):
server_id = str(uuid.uuid4())
self.m.StubOutWithMock(
self.engine.software_config,
'_push_metadata_software_deployments')
# push on create
self.engine.software_config._push_metadata_software_deployments(
self.ctx, server_id).AndReturn(None)
# push on update with new config_id
self.engine.software_config._push_metadata_software_deployments(
self.ctx, server_id).AndReturn(None)
self.m.ReplayAll()
deployment = self._create_software_deployment(server_id=server_id)
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployment_action = deployment['action']
self.assertEqual('INIT', deployment_action)
config_id = deployment['config_id']
self.assertIsNotNone(config_id)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id=deployment_id, config_id=config_id,
input_values={}, output_values={}, action='DEPLOY',
status='WAITING', status_reason='', updated_at=None)
self.assertIsNotNone(updated)
self.assertEqual(config_id, updated['config_id'])
self.assertEqual('DEPLOY', updated['action'])
self.assertEqual('WAITING', updated['status'])
self.m.VerifyAll()
def test_update_software_deployment_status(self):
server_id = str(uuid.uuid4())
self.m.StubOutWithMock(
self.engine.software_config,
'_push_metadata_software_deployments')
# push on create
self.engine.software_config._push_metadata_software_deployments(
self.ctx, server_id).AndReturn(None)
# _push_metadata_software_deployments should not be called
# on update because config_id isn't being updated
self.m.ReplayAll()
deployment = self._create_software_deployment(server_id=server_id)
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployment_action = deployment['action']
self.assertEqual('INIT', deployment_action)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id=deployment_id, config_id=None,
input_values=None, output_values={}, action='DEPLOY',
status='WAITING', status_reason='', updated_at=None)
self.assertIsNotNone(updated)
self.assertEqual('DEPLOY', updated['action'])
self.assertEqual('WAITING', updated['status'])
self.m.VerifyAll()
def test_update_software_deployment_fields(self):
deployment = self._create_software_deployment()
deployment_id = deployment['id']
config_id = deployment['config_id']
def check_software_deployment_updated(**kwargs):
values = {
'config_id': None,
'input_values': {},
'output_values': {},
'action': {},
'status': 'WAITING',
'status_reason': ''
}
values.update(kwargs)
updated = self.engine.update_software_deployment(
self.ctx, deployment_id, updated_at=None, **values)
for key, value in six.iteritems(kwargs):
self.assertEqual(value, updated[key])
check_software_deployment_updated(config_id=config_id)
check_software_deployment_updated(input_values={'foo': 'fooooo'})
check_software_deployment_updated(output_values={'bar': 'baaaaa'})
check_software_deployment_updated(action='DEPLOY')
check_software_deployment_updated(status='COMPLETE')
check_software_deployment_updated(status_reason='Done!')
def test_delete_software_deployment(self):
deployment_id = str(uuid.uuid4())
ex = self.assertRaises(dispatcher.ExpectedException,
self.engine.delete_software_deployment,
self.ctx, deployment_id)
self.assertEqual(exception.NotFound, ex.exc_info[0])
deployment = self._create_software_deployment()
self.assertIsNotNone(deployment)
deployment_id = deployment['id']
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
deployment_ids = [x['id'] for x in deployments]
self.assertIn(deployment_id, deployment_ids)
self.engine.delete_software_deployment(self.ctx, deployment_id)
deployments = self.engine.list_software_deployments(
self.ctx, server_id=None)
deployment_ids = [x['id'] for x in deployments]
self.assertNotIn(deployment_id, deployment_ids)
@mock.patch.object(service_software_config.SoftwareConfigService,
'metadata_software_deployments')
@mock.patch.object(service_software_config.resource_object.Resource,
'get_by_physical_resource_id')
@mock.patch.object(service_software_config.requests, 'put')
def test_push_metadata_software_deployments(self, put, res_get, md_sd):
rs = mock.Mock()
rs.rsrc_metadata = {'original': 'metadata'}
rs.data = []
res_get.return_value = rs
deployments = {'deploy': 'this'}
md_sd.return_value = deployments
result_metadata = {
'original': 'metadata',
'deployments': {'deploy': 'this'}
}
self.engine.software_config._push_metadata_software_deployments(
self.ctx, '1234')
rs.update_and_save.assert_called_once_with(
{'rsrc_metadata': result_metadata})
put.side_effect = Exception('Unexpected requests.put')
@mock.patch.object(service_software_config.SoftwareConfigService,
'metadata_software_deployments')
@mock.patch.object(service_software_config.resource_object.Resource,
'get_by_physical_resource_id')
@mock.patch.object(service_software_config.requests, 'put')
def test_push_metadata_software_deployments_temp_url(
self, put, res_get, md_sd):
rs = mock.Mock()
rs.rsrc_metadata = {'original': 'metadata'}
rd = mock.Mock()
rd.key = 'metadata_put_url'
rd.value = 'http://192.168.2.2/foo/bar'
rs.data = [rd]
res_get.return_value = rs
deployments = {'deploy': 'this'}
md_sd.return_value = deployments
result_metadata = {
'original': 'metadata',
'deployments': {'deploy': 'this'}
}
self.engine.software_config._push_metadata_software_deployments(
self.ctx, '1234')
rs.update_and_save.assert_called_once_with(
{'rsrc_metadata': result_metadata})
put.assert_called_once_with(
'http://192.168.2.2/foo/bar', json.dumps(result_metadata))
@mock.patch.object(service_software_config.SoftwareConfigService,
'signal_software_deployment')
@mock.patch.object(swift.SwiftClientPlugin, '_create')
def test_refresh_software_deployment(self, scc, ssd):
temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c'
'?temp_url_sig=ctemp_url_expires=1234')
container = 'b'
object_name = 'c'
config = self._create_software_config(inputs=[
{
'name': 'deploy_signal_transport',
'type': 'String',
'value': 'TEMP_URL_SIGNAL'
}, {
'name': 'deploy_signal_id',
'type': 'String',
'value': temp_url
}
])
timeutils.set_time_override(
datetime.datetime(2013, 1, 23, 22, 48, 5, 0))
self.addCleanup(timeutils.clear_time_override)
now = timeutils.utcnow()
then = now - datetime.timedelta(0, 60)
last_modified_1 = 'Wed, 23 Jan 2013 22:47:05 GMT'
last_modified_2 = 'Wed, 23 Jan 2013 22:48:05 GMT'
sc = mock.MagicMock()
headers = {
'last-modified': last_modified_1
}
sc.head_object.return_value = headers
sc.get_object.return_value = (headers, '{"foo": "bar"}')
scc.return_value = sc
deployment = self._create_software_deployment(
status='IN_PROGRESS', config_id=config['id'])
deployment_id = six.text_type(deployment['id'])
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
# poll with missing object
swift_exc = swift.SwiftClientPlugin.exceptions_module
sc.head_object.side_effect = swift_exc.ClientException(
'Not found', http_status=404)
self.assertEqual(
sd,
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url))
sc.head_object.assert_called_once_with(container, object_name)
# no call to get_object or signal_last_modified
self.assertEqual([], sc.get_object.mock_calls)
self.assertEqual([], ssd.mock_calls)
# poll with other error
sc.head_object.side_effect = swift_exc.ClientException(
'Ouch', http_status=409)
self.assertRaises(
swift_exc.ClientException,
self.engine.software_config._refresh_software_deployment,
self.ctx,
sd,
temp_url)
# no call to get_object or signal_last_modified
self.assertEqual([], sc.get_object.mock_calls)
self.assertEqual([], ssd.mock_calls)
sc.head_object.side_effect = None
# first poll populates data signal_last_modified
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
sc.head_object.assert_called_with(container, object_name)
sc.get_object.assert_called_once_with(container, object_name)
# signal_software_deployment called with signal
ssd.assert_called_once_with(self.ctx, deployment_id, {u"foo": u"bar"},
timeutils.strtime(then))
# second poll updated_at populated with first poll last-modified
software_deployment_object.SoftwareDeployment.update_by_id(
self.ctx, deployment_id, {'updated_at': then})
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.assertEqual(then, sd.updated_at)
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
sc.get_object.assert_called_once_with(container, object_name)
# signal_software_deployment has not been called again
ssd.assert_called_once_with(self.ctx, deployment_id, {"foo": "bar"},
timeutils.strtime(then))
# third poll last-modified changed, new signal
headers['last-modified'] = last_modified_2
sc.head_object.return_value = headers
sc.get_object.return_value = (headers, '{"bar": "baz"}')
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
# two calls to signal_software_deployment, for then and now
self.assertEqual(2, len(ssd.mock_calls))
ssd.assert_called_with(self.ctx, deployment_id, {"bar": "baz"},
timeutils.strtime(now))
# four polls result in only two signals, for then and now
software_deployment_object.SoftwareDeployment.update_by_id(
self.ctx, deployment_id, {'updated_at': now})
sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id)
self.engine.software_config._refresh_software_deployment(
self.ctx, sd, temp_url)
self.assertEqual(2, len(ssd.mock_calls))
class ThreadGroupManagerTest(common.HeatTestCase):
def setUp(self):
super(ThreadGroupManagerTest, self).setUp()