
With the addition of Puppet support, it's possible that the files returned for the stack will be nested into directories. This directory information will be carried in the key values for the templates dict (e.g. 'hieradata/common.yaml' : <contents>). This change looks for one or more directories in the template name and creates the necessary structure in the template output directory. Change-Id: Ic7dc65427ab9fdb981db1663c400e4e0cc3cc53d
341 lines
14 KiB
Python
341 lines
14 KiB
Python
# 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 mock
|
|
import six
|
|
|
|
import tuskarclient.tests.utils as tutils
|
|
from tuskarclient.v2 import plans_shell
|
|
|
|
|
|
def empty_args():
|
|
args = mock.Mock(spec=[])
|
|
for attr in ['uuid', 'name', 'description', 'parameters']:
|
|
setattr(args, attr, None)
|
|
return args
|
|
|
|
|
|
def mock_plan():
|
|
plan = mock.Mock()
|
|
plan.uuid = '5'
|
|
plan.name = 'My Plan'
|
|
plan.parameters = []
|
|
plan.parameters.append({'name': 'compute-1::count', 'value': '2'})
|
|
plan.parameters.append({'name': 'compute-1::Flavor', 'value': 'baremetal'})
|
|
plan.to_dict.return_value = {
|
|
'uuid': 5,
|
|
'name': 'My Plan',
|
|
'parameters': plan.parameters,
|
|
}
|
|
return plan
|
|
|
|
|
|
class BasePlansShellTest(tutils.TestCase):
|
|
|
|
def setUp(self):
|
|
super(BasePlansShellTest, self).setUp()
|
|
self.outfile = six.StringIO()
|
|
self.tuskar = mock.MagicMock()
|
|
self.shell = plans_shell
|
|
|
|
|
|
class PlansShellTest(BasePlansShellTest):
|
|
|
|
@mock.patch('tuskarclient.common.formatting.print_list')
|
|
def test_plan_list(self, mock_print_list):
|
|
args = empty_args()
|
|
|
|
self.shell.do_plan_list(self.tuskar, args, outfile=self.outfile)
|
|
# testing the other arguments would be just copy-paste
|
|
mock_print_list.assert_called_with(
|
|
self.tuskar.plans.list.return_value, mock.ANY, mock.ANY,
|
|
outfile=self.outfile
|
|
)
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_plan_show(self, mock_print_summary, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
args.verbose = False
|
|
|
|
self.shell.do_plan_show(self.tuskar, args, outfile=self.outfile)
|
|
mock_find_resource.assert_called_with(self.tuskar.plans, '5')
|
|
mock_print_summary.assert_called_with(mock_find_resource.return_value,
|
|
outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
@mock.patch('tuskarclient.common.formatting.print_dict')
|
|
def test_plan_show_scale(self, mock_print_dict, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
|
|
self.shell.do_plan_show_scale(self.tuskar, args, outfile=self.outfile)
|
|
mock_find_resource.assert_called_with(self.tuskar.plans, '5')
|
|
mock_print_dict.assert_called_with({'compute-1': '2'},
|
|
outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
@mock.patch('tuskarclient.common.formatting.print_dict')
|
|
def test_plan_show_flavors(self, mock_print_dict, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
|
|
self.shell.do_plan_show_flavors(self.tuskar, args,
|
|
outfile=self.outfile)
|
|
mock_find_resource.assert_called_with(self.tuskar.plans, '5')
|
|
mock_print_dict.assert_called_with({'compute-1': 'baremetal'},
|
|
outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
def test_plan_delete(self, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
|
|
self.shell.do_plan_delete(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.delete.assert_called_with('5')
|
|
self.assertEqual('Deleted Plan "My Plan".\n',
|
|
self.outfile.getvalue())
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_plan_create(self, mock_print_summary):
|
|
args = empty_args()
|
|
args.name = 'my_plan'
|
|
args.description = 'my plan description'
|
|
|
|
self.shell.do_plan_create(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.create.assert_called_with(
|
|
name='my_plan',
|
|
description='my plan description'
|
|
)
|
|
mock_print_summary.assert_called_with(
|
|
self.tuskar.plans.create.return_value, outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_add_role(self, mock_print_summary):
|
|
args = empty_args()
|
|
args.plan_uuid = '42'
|
|
args.role_uuid = 'role_uuid'
|
|
|
|
self.shell.do_plan_add_role(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.add_role.assert_called_with('42', 'role_uuid')
|
|
|
|
mock_print_summary.assert_called_with(
|
|
self.tuskar.plans.add_role.return_value, outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_remove_role(self, mock_print_summary):
|
|
args = empty_args()
|
|
args.plan_uuid = '42'
|
|
args.role_uuid = 'role_uuid'
|
|
|
|
self.shell.do_plan_remove_role(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.remove_role.assert_called_with('42', 'role_uuid')
|
|
|
|
mock_print_summary.assert_called_with(
|
|
self.tuskar.plans.remove_role.return_value, outfile=self.outfile)
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
def test_plan_scale(self, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
role = mock.Mock()
|
|
role.name = 'compute'
|
|
role.version = 1
|
|
self.tuskar.roles.list.return_value = [role]
|
|
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.role_name = 'compute-1'
|
|
args.count = '9'
|
|
|
|
parameters = [{'name': 'compute-1::count', 'value': '9'}]
|
|
|
|
self.shell.do_plan_scale(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.patch.assert_called_once()
|
|
self.assertEqual('plan_uuid', self.tuskar.plans.patch.call_args[0][0])
|
|
self.assertEqual(
|
|
sorted(parameters, key=lambda k: k['name']),
|
|
sorted(self.tuskar.plans.patch.call_args[0][1],
|
|
key=lambda k: k['name']))
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
def test_plan_flavor(self, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
role = mock.Mock()
|
|
role.name = 'compute'
|
|
role.version = 1
|
|
self.tuskar.roles.list.return_value = [role]
|
|
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.role_name = 'compute-1'
|
|
args.flavor = 'baremetalssd'
|
|
|
|
parameters = [{'name': 'compute-1::Flavor', 'value': 'baremetalssd'}]
|
|
|
|
self.shell.do_plan_flavor(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.patch.assert_called_once()
|
|
self.assertEqual('plan_uuid', self.tuskar.plans.patch.call_args[0][0])
|
|
self.assertEqual(
|
|
sorted(parameters, key=lambda k: k['name']),
|
|
sorted(self.tuskar.plans.patch.call_args[0][1],
|
|
key=lambda k: k['name']))
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_plan_patch(self, mock_print_summary):
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.parameters = ['foo_name=foo_value',
|
|
'bar_name=bar_value']
|
|
args.attributes = None
|
|
parameters = [{'name': 'foo_name', 'value': 'foo_value'},
|
|
{'name': 'bar_name', 'value': 'bar_value'}]
|
|
self.shell.do_plan_patch(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.patch.assert_called_once()
|
|
self.assertEqual('plan_uuid',
|
|
self.tuskar.plans.patch.call_args[0][0])
|
|
self.assertEqual(
|
|
sorted(parameters, key=lambda k: k['name']),
|
|
sorted(self.tuskar.plans.patch.call_args[0][1],
|
|
key=lambda k: k['name']))
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_summary')
|
|
def test_plan_patch_deprecated(self, mock_print_summary):
|
|
"""Test plan_patch with the deprecated --attribute flag."""
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.attributes = ['foo_name=foo_value',
|
|
'bar_name=bar_value']
|
|
args.parameters = None
|
|
parameters = [{'name': 'foo_name', 'value': 'foo_value'},
|
|
{'name': 'bar_name', 'value': 'bar_value'}]
|
|
self.shell.do_plan_patch(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.patch.assert_called_once()
|
|
self.assertEqual('plan_uuid',
|
|
self.tuskar.plans.patch.call_args[0][0])
|
|
self.assertEqual(
|
|
sorted(parameters, key=lambda k: k['name']),
|
|
sorted(self.tuskar.plans.patch.call_args[0][1],
|
|
key=lambda k: k['name']))
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print_plan_detail')
|
|
def test_plan_update(self, mock_print_detail):
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.parameters = ['foo_name=foo_value',
|
|
'bar_name=bar_value']
|
|
parameters = [{'name': 'foo_name', 'value': 'foo_value'},
|
|
{'name': 'bar_name', 'value': 'bar_value'}]
|
|
args.attributes = None
|
|
self.shell.do_plan_update(self.tuskar, args, outfile=self.outfile)
|
|
self.tuskar.plans.patch.assert_called_once()
|
|
self.assertEqual('plan_uuid',
|
|
self.tuskar.plans.patch.call_args[0][0])
|
|
self.assertEqual(
|
|
sorted(parameters, key=lambda k: k['name']),
|
|
sorted(self.tuskar.plans.patch.call_args[0][1],
|
|
key=lambda k: k['name']))
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
def test_print_plan_summary(self, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
args.verbose = False
|
|
|
|
self.shell.do_plan_show(self.tuskar, args, outfile=self.outfile)
|
|
mock_find_resource.assert_called_with(self.tuskar.plans, '5')
|
|
|
|
@mock.patch('tuskarclient.common.utils.find_resource')
|
|
def test_print_plan_detail(self, mock_find_resource):
|
|
mock_find_resource.return_value = mock_plan()
|
|
args = empty_args()
|
|
args.plan = '5'
|
|
args.verbose = True
|
|
|
|
self.shell.do_plan_show(self.tuskar, args, outfile=self.outfile)
|
|
mock_find_resource.assert_called_with(self.tuskar.plans, '5')
|
|
|
|
def test_filter_parameters_to_dict(self):
|
|
parameters = [{'name': 'compute-1::count', 'value': '2'}]
|
|
self.assertEqual(
|
|
self.shell.filter_parameters_to_dict(parameters, 'count'),
|
|
{'compute-1': '2'}
|
|
)
|
|
|
|
@mock.patch('tuskarclient.v2.plans_shell.print', create=True)
|
|
@mock.patch('tuskarclient.v2.plans_shell.os.mkdir', create=True)
|
|
@mock.patch('tuskarclient.v2.plans_shell.os.path.isdir', create=True)
|
|
@mock.patch('tuskarclient.v2.plans_shell.open', create=True)
|
|
@mock.patch('tuskarclient.v2.plans_shell.os.path.exists', create=True)
|
|
@mock.patch('tuskarclient.v2.plans_shell.os.makedirs', create=True)
|
|
def test_plan_templates(
|
|
self, mock_makedirs, mock_exists, mock_open, mock_isdir,
|
|
mock_mkdir, mock_print):
|
|
args = empty_args()
|
|
args.plan_uuid = 'plan_uuid'
|
|
args.output_dir = 'outdir/subdir'
|
|
|
|
# Simulate the first exists check being false and the subsequent check
|
|
# being true so as to exercise that makesdirs is only called once
|
|
# per nested directory.
|
|
exists_return_values = [False, True]
|
|
|
|
def toggle_exists_result(*e_args, **e_kwargs):
|
|
return exists_return_values.pop(0)
|
|
mock_exists.side_effect = toggle_exists_result
|
|
|
|
mock_isdir.return_value = False
|
|
self.tuskar.plans.templates.return_value = {
|
|
'name_foo': 'value_foo',
|
|
'name_bar': 'value_bar',
|
|
'nested/name_baz': 'value_baz',
|
|
'nested/name_zom': 'value_zom'
|
|
}
|
|
|
|
self.shell.do_plan_templates(self.tuskar, args, outfile=self.outfile)
|
|
|
|
# Initial check and creation of the output directory
|
|
mock_isdir.assert_any_call('outdir/subdir')
|
|
mock_mkdir.assert_any_call('outdir/subdir')
|
|
|
|
# Checks and creation of nested directory
|
|
self.assertEqual(mock_exists.call_count, 2)
|
|
self.assertEqual(mock_makedirs.call_count, 1)
|
|
mock_makedirs.assert_called_with('outdir/subdir/nested')
|
|
|
|
self.tuskar.plans.templates.assert_called_with('plan_uuid')
|
|
|
|
mock_open.assert_any_call('outdir/subdir/name_foo', 'w+')
|
|
mock_open.assert_any_call('outdir/subdir/name_bar', 'w+')
|
|
mock_open.assert_any_call('outdir/subdir/nested/name_baz', 'w+')
|
|
mock_open.assert_any_call('outdir/subdir/nested/name_zom', 'w+')
|
|
self.assertEqual(mock_open.call_count, 4)
|
|
|
|
mock_opened_file = mock_open.return_value.__enter__.return_value
|
|
mock_opened_file.write.assert_any_call('value_foo')
|
|
mock_opened_file.write.assert_any_call('value_bar')
|
|
mock_opened_file.write.assert_any_call('value_baz')
|
|
mock_opened_file.write.assert_any_call('value_zom')
|
|
self.assertEqual(mock_opened_file.write.call_count, 4)
|
|
|
|
mock_print.assert_any_call('The following templates will be written:')
|
|
mock_print.assert_any_call('outdir/subdir/name_foo')
|
|
mock_print.assert_any_call('outdir/subdir/name_bar')
|
|
mock_print.assert_any_call('outdir/subdir/nested/name_baz')
|
|
mock_print.assert_any_call('outdir/subdir/nested/name_zom')
|
|
self.assertEqual(mock_print.call_count, 5)
|