Merge "Allow Bay templates to include Heat environments"
This commit is contained in:
commit
af7cd43652
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from heatclient.common import template_utils
|
||||
|
@ -73,10 +74,25 @@ def _extract_template_definition(context, bay, scale_manager=None):
|
|||
scale_manager=scale_manager)
|
||||
|
||||
|
||||
def _get_env_files(template_path, env_rel_paths):
|
||||
template_dir = os.path.dirname(template_path)
|
||||
env_abs_paths = [os.path.join(template_dir, f) for f in env_rel_paths]
|
||||
environment_files = []
|
||||
env_map, merged_env = (
|
||||
template_utils.process_multiple_environments_and_files(
|
||||
env_paths=env_abs_paths, env_list_tracker=environment_files))
|
||||
return environment_files, env_map
|
||||
|
||||
|
||||
def _create_stack(context, osc, bay, bay_create_timeout):
|
||||
template_path, heat_params = _extract_template_definition(context, bay)
|
||||
template_path, heat_params, env_files = (
|
||||
_extract_template_definition(context, bay))
|
||||
|
||||
tpl_files, template = template_utils.get_template_contents(template_path)
|
||||
|
||||
environment_files, env_map = _get_env_files(template_path, env_files)
|
||||
tpl_files.update(env_map)
|
||||
|
||||
# Make sure no duplicate stack name
|
||||
stack_name = '%s-%s' % (bay.name, short_id.generate_id())
|
||||
if bay_create_timeout:
|
||||
|
@ -90,6 +106,7 @@ def _create_stack(context, osc, bay, bay_create_timeout):
|
|||
fields = {
|
||||
'stack_name': stack_name,
|
||||
'parameters': heat_params,
|
||||
'environment_files': environment_files,
|
||||
'template': template,
|
||||
'files': tpl_files,
|
||||
'timeout_mins': heat_timeout
|
||||
|
@ -100,12 +117,16 @@ def _create_stack(context, osc, bay, bay_create_timeout):
|
|||
|
||||
|
||||
def _update_stack(context, osc, bay, scale_manager=None):
|
||||
template_path, heat_params = _extract_template_definition(
|
||||
template_path, heat_params, env_files = _extract_template_definition(
|
||||
context, bay, scale_manager=scale_manager)
|
||||
|
||||
tpl_files, template = template_utils.get_template_contents(template_path)
|
||||
environment_files, env_map = _get_env_files(template_path, env_files)
|
||||
tpl_files.update(env_map)
|
||||
|
||||
fields = {
|
||||
'parameters': heat_params,
|
||||
'environment_files': environment_files,
|
||||
'template': template,
|
||||
'files': tpl_files
|
||||
}
|
||||
|
|
|
@ -305,6 +305,18 @@ class TemplateDefinition(object):
|
|||
|
||||
return template_params
|
||||
|
||||
def get_env_files(self, baymodel):
|
||||
"""Collects stack environment files based upon Baymodel attributes.
|
||||
|
||||
Base implementation returns no files (empty list). Meant to be
|
||||
overridden by subclasses.
|
||||
|
||||
:param baymodel: Baymodel to collect environment files for
|
||||
|
||||
:return: list of relative paths to environment files
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_heat_param(self, bay_attr=None, baymodel_attr=None):
|
||||
"""Returns stack param name.
|
||||
|
||||
|
@ -331,8 +343,9 @@ class TemplateDefinition(object):
|
|||
pass
|
||||
|
||||
def extract_definition(self, context, baymodel, bay, **kwargs):
|
||||
return self.template_path, self.get_params(context, baymodel, bay,
|
||||
**kwargs)
|
||||
return (self.template_path,
|
||||
self.get_params(context, baymodel, bay, **kwargs),
|
||||
self.get_env_files(baymodel))
|
||||
|
||||
|
||||
class BaseTemplateDefinition(TemplateDefinition):
|
||||
|
|
|
@ -367,6 +367,87 @@ class TestHandler(db_base.DbTestCase):
|
|||
self.assertEqual(
|
||||
taxonomy.OUTCOME_FAILURE, notifications[1].payload['outcome'])
|
||||
|
||||
@patch('magnum.conductor.handlers.bay_conductor.HeatPoller')
|
||||
@patch('heatclient.common.template_utils'
|
||||
'.process_multiple_environments_and_files')
|
||||
@patch('heatclient.common.template_utils.get_template_contents')
|
||||
@patch('magnum.conductor.handlers.bay_conductor'
|
||||
'._extract_template_definition')
|
||||
@patch('magnum.conductor.handlers.bay_conductor.trust_manager')
|
||||
@patch('magnum.conductor.handlers.bay_conductor.cert_manager')
|
||||
@patch('magnum.conductor.handlers.bay_conductor.short_id')
|
||||
@patch('magnum.conductor.handlers.bay_conductor.uuid')
|
||||
@patch('magnum.common.clients.OpenStackClients')
|
||||
def test_create_with_environment(self,
|
||||
mock_openstack_client_class,
|
||||
mock_uuid,
|
||||
mock_short_id,
|
||||
mock_cert_manager,
|
||||
mock_trust_manager,
|
||||
mock_extract_tmpl_def,
|
||||
mock_get_template_contents,
|
||||
mock_process_mult,
|
||||
mock_heat_poller_class):
|
||||
timeout = 15
|
||||
self.bay.baymodel_id = self.baymodel.uuid
|
||||
test_uuid = uuid.uuid4()
|
||||
mock_uuid.uuid4.return_value = test_uuid
|
||||
bay_name = self.bay.name
|
||||
mock_short_id.generate_id.return_value = 'short_id'
|
||||
mock_poller = mock.MagicMock()
|
||||
mock_poller.poll_and_check.return_value = loopingcall.LoopingCallDone()
|
||||
mock_heat_poller_class.return_value = mock_poller
|
||||
|
||||
mock_extract_tmpl_def.return_value = (
|
||||
'the/template/path.yaml',
|
||||
{'heat_param_1': 'foo', 'heat_param_2': 'bar'},
|
||||
['env_file_1', 'env_file_2'])
|
||||
|
||||
mock_get_template_contents.return_value = (
|
||||
{'tmpl_file_1': 'some content',
|
||||
'tmpl_file_2': 'some more content'},
|
||||
'some template yaml')
|
||||
|
||||
def do_mock_process_mult(env_paths=None, env_list_tracker=None):
|
||||
self.assertEqual(env_list_tracker, [])
|
||||
for f in env_paths:
|
||||
env_list_tracker.append('file:///' + f)
|
||||
env_map = {path: 'content of ' + path for path in env_list_tracker}
|
||||
return (env_map, None)
|
||||
|
||||
mock_process_mult.side_effect = do_mock_process_mult
|
||||
|
||||
mock_hc = mock.Mock()
|
||||
mock_hc.stacks.create.return_value = {'stack': {'id': 'stack-id'}}
|
||||
|
||||
osc = mock.Mock()
|
||||
osc.heat.return_value = mock_hc
|
||||
mock_openstack_client_class.return_value = osc
|
||||
|
||||
self.handler.bay_create(self.context, self.bay, timeout)
|
||||
|
||||
mock_extract_tmpl_def.assert_called_once_with(self.context, self.bay)
|
||||
mock_get_template_contents.assert_called_once_with(
|
||||
'the/template/path.yaml')
|
||||
mock_process_mult.assert_called_once_with(
|
||||
env_paths=['the/template/env_file_1', 'the/template/env_file_2'],
|
||||
env_list_tracker=mock.ANY)
|
||||
mock_hc.stacks.create.assert_called_once_with(
|
||||
environment_files=['file:///the/template/env_file_1',
|
||||
'file:///the/template/env_file_2'],
|
||||
files={
|
||||
'tmpl_file_1': 'some content',
|
||||
'tmpl_file_2': 'some more content',
|
||||
'file:///the/template/env_file_1':
|
||||
'content of file:///the/template/env_file_1',
|
||||
'file:///the/template/env_file_2':
|
||||
'content of file:///the/template/env_file_2'
|
||||
},
|
||||
parameters={'heat_param_1': 'foo', 'heat_param_2': 'bar'},
|
||||
stack_name=('%s-short_id' % bay_name),
|
||||
template='some template yaml',
|
||||
timeout_mins=timeout)
|
||||
|
||||
@patch('magnum.common.clients.OpenStackClients')
|
||||
def test_bay_delete(self, mock_openstack_client_class):
|
||||
osc = mock.MagicMock()
|
||||
|
|
|
@ -101,8 +101,9 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
mapping = {
|
||||
'dns_nameserver': 'dns_nameserver',
|
||||
|
@ -167,6 +168,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
expected.pop(mapping[missing_attr], None)
|
||||
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_with_registry(
|
||||
|
@ -182,8 +184,9 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
group='docker_registry')
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'auth_url': 'http://192.168.10.10:5000/v3',
|
||||
|
@ -224,6 +227,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
}
|
||||
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_coreos_with_disovery(
|
||||
|
@ -235,8 +239,9 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -269,6 +274,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'insecure_registry_url': '10.0.0.1:5000',
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('requests.get')
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
|
@ -285,8 +291,9 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -319,6 +326,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'insecure_registry_url': '10.0.0.1:5000',
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_without_dns(
|
||||
|
@ -411,8 +419,9 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
reqget.return_value = mock_req
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -450,6 +459,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'insecure_registry_url': '10.0.0.1:5000',
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
reqget.assert_called_once_with('http://etcd/test?size=1')
|
||||
|
||||
@patch('magnum.common.short_id.generate_id')
|
||||
|
@ -471,7 +481,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
mock_get_template_contents.return_value = [
|
||||
mock_tpl_files, expected_template_contents]
|
||||
mock_extract_template_definition.return_value = ('template/path',
|
||||
{})
|
||||
{}, [])
|
||||
mock_heat_client = mock.MagicMock()
|
||||
mock_osc = mock.MagicMock()
|
||||
mock_osc.heat.return_value = mock_heat_client
|
||||
|
@ -486,6 +496,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'parameters': {},
|
||||
'template': expected_template_contents,
|
||||
'files': {},
|
||||
'environment_files': [],
|
||||
'timeout_mins': expected_timeout
|
||||
}
|
||||
mock_heat_client.stacks.create.assert_called_once_with(**expected_args)
|
||||
|
@ -510,7 +521,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
mock_get_template_contents.return_value = [
|
||||
mock_tpl_files, expected_template_contents]
|
||||
mock_extract_template_definition.return_value = ('template/path',
|
||||
{})
|
||||
{}, [])
|
||||
mock_heat_client = mock.MagicMock()
|
||||
mock_osc = mock.MagicMock()
|
||||
mock_osc.heat.return_value = mock_heat_client
|
||||
|
@ -525,6 +536,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'parameters': {},
|
||||
'template': expected_template_contents,
|
||||
'files': {},
|
||||
'environment_files': [],
|
||||
'timeout_mins': expected_timeout
|
||||
}
|
||||
mock_heat_client.stacks.create.assert_called_once_with(**expected_args)
|
||||
|
@ -550,7 +562,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
mock_get_template_contents.return_value = [
|
||||
mock_tpl_files, expected_template_contents]
|
||||
mock_extract_template_definition.return_value = ('template/path',
|
||||
{})
|
||||
{}, [])
|
||||
mock_heat_client = mock.MagicMock()
|
||||
mock_osc = mock.MagicMock()
|
||||
mock_osc.heat.return_value = mock_heat_client
|
||||
|
@ -565,6 +577,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
'parameters': {},
|
||||
'template': expected_template_contents,
|
||||
'files': {},
|
||||
'environment_files': [],
|
||||
'timeout_mins': expected_timeout
|
||||
}
|
||||
mock_heat_client.stacks.create.assert_called_once_with(**expected_args)
|
||||
|
@ -583,7 +596,7 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
mock_get_template_contents.return_value = [
|
||||
mock_tpl_files, expected_template_contents]
|
||||
mock_extract_template_definition.return_value = ('template/path',
|
||||
{})
|
||||
{}, [])
|
||||
mock_heat_client = mock.MagicMock()
|
||||
mock_osc = mock.MagicMock()
|
||||
mock_osc.heat.return_value = mock_heat_client
|
||||
|
@ -595,7 +608,8 @@ class TestBayConductorWithK8s(base.TestCase):
|
|||
expected_args = {
|
||||
'parameters': {},
|
||||
'template': expected_template_contents,
|
||||
'files': {}
|
||||
'files': {},
|
||||
'environment_files': []
|
||||
}
|
||||
mock_heat_client.stacks.update.assert_called_once_with(mock_stack_id,
|
||||
**expected_args)
|
||||
|
|
|
@ -85,8 +85,9 @@ class TestBayConductorWithMesos(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -119,6 +120,7 @@ class TestBayConductorWithMesos(base.TestCase):
|
|||
'mesos_slave_image_providers': 'docker'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_only_required(
|
||||
|
@ -135,8 +137,9 @@ class TestBayConductorWithMesos(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -161,6 +164,7 @@ class TestBayConductorWithMesos(base.TestCase):
|
|||
'mesos_slave_image_providers': 'docker'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.conductor.utils.retrieve_baymodel')
|
||||
@patch('oslo_config.cfg')
|
||||
|
|
|
@ -84,8 +84,9 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -118,6 +119,7 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
'auth_url': 'http://192.168.10.10:5000/v3'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_with_registry(
|
||||
|
@ -133,8 +135,9 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
group='docker_registry')
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -169,6 +172,7 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
'docker_storage_driver': 'devicemapper'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.objects.BayModel.get_by_uuid')
|
||||
def test_extract_template_definition_only_required(
|
||||
|
@ -188,8 +192,9 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
bay = objects.Bay(self.context, **self.bay_dict)
|
||||
|
||||
(template_path,
|
||||
definition) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
definition,
|
||||
env_files) = bay_conductor._extract_template_definition(self.context,
|
||||
bay)
|
||||
|
||||
expected = {
|
||||
'ssh_key_name': 'keypair_id',
|
||||
|
@ -212,6 +217,7 @@ class TestBayConductorWithSwarm(base.TestCase):
|
|||
'auth_url': 'http://192.168.10.10:5000/v3'
|
||||
}
|
||||
self.assertEqual(expected, definition)
|
||||
self.assertEqual([], env_files)
|
||||
|
||||
@patch('magnum.conductor.utils.retrieve_baymodel')
|
||||
@patch('oslo_config.cfg')
|
||||
|
|
Loading…
Reference in New Issue