|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
11b429 |
import os
|
|
Packit Service |
751c4a |
import io
|
|
Packit Service |
751c4a |
from collections import namedtuple
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit.cmd import main as cli
|
|
Packit Service |
a04d08 |
from cloudinit.tests import helpers as test_helpers
|
|
Packit Service |
a04d08 |
from cloudinit.util import load_file, load_json
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
mock = test_helpers.mock
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
class TestCLI(test_helpers.FilesystemMockingTestCase):
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
with_logs = True
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def setUp(self):
|
|
Packit Service |
a04d08 |
super(TestCLI, self).setUp()
|
|
Packit Service |
751c4a |
self.stderr = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stderr=self.stderr)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _call_main(self, sysv_args=None):
|
|
Packit Service |
a04d08 |
if not sysv_args:
|
|
Packit Service |
a04d08 |
sysv_args = ['cloud-init']
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
return cli.main(sysv_args=sysv_args)
|
|
Packit Service |
a04d08 |
except SystemExit as e:
|
|
Packit Service |
a04d08 |
return e.code
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_status_wrapper_errors_on_invalid_name(self):
|
|
Packit Service |
a04d08 |
"""status_wrapper will error when the name parameter is not valid.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Valid name values are only init and modules.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
tmpd = self.tmp_dir()
|
|
Packit Service |
a04d08 |
data_d = self.tmp_path('data', tmpd)
|
|
Packit Service |
a04d08 |
link_d = self.tmp_path('link', tmpd)
|
|
Packit Service |
a04d08 |
FakeArgs = namedtuple('FakeArgs', ['action', 'local', 'mode'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def myaction():
|
|
Packit Service |
a04d08 |
raise Exception('Should not call myaction')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
myargs = FakeArgs(('doesnotmatter', myaction), False, 'bogusmode')
|
|
Packit Service |
a04d08 |
with self.assertRaises(ValueError) as cm:
|
|
Packit Service |
a04d08 |
cli.status_wrapper('init1', myargs, data_d, link_d)
|
|
Packit Service |
a04d08 |
self.assertEqual('unknown name: init1', str(cm.exception))
|
|
Packit Service |
a04d08 |
self.assertNotIn('Should not call myaction', self.logs.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_status_wrapper_errors_on_invalid_modes(self):
|
|
Packit Service |
a04d08 |
"""status_wrapper will error if a parameter combination is invalid."""
|
|
Packit Service |
a04d08 |
tmpd = self.tmp_dir()
|
|
Packit Service |
a04d08 |
data_d = self.tmp_path('data', tmpd)
|
|
Packit Service |
a04d08 |
link_d = self.tmp_path('link', tmpd)
|
|
Packit Service |
a04d08 |
FakeArgs = namedtuple('FakeArgs', ['action', 'local', 'mode'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def myaction():
|
|
Packit Service |
a04d08 |
raise Exception('Should not call myaction')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
myargs = FakeArgs(('modules_name', myaction), False, 'bogusmode')
|
|
Packit Service |
a04d08 |
with self.assertRaises(ValueError) as cm:
|
|
Packit Service |
a04d08 |
cli.status_wrapper('modules', myargs, data_d, link_d)
|
|
Packit Service |
a04d08 |
self.assertEqual(
|
|
Packit Service |
a04d08 |
"Invalid cloud init mode specified 'modules-bogusmode'",
|
|
Packit Service |
a04d08 |
str(cm.exception))
|
|
Packit Service |
a04d08 |
self.assertNotIn('Should not call myaction', self.logs.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_status_wrapper_init_local_writes_fresh_status_info(self):
|
|
Packit Service |
a04d08 |
"""When running in init-local mode, status_wrapper writes status.json.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
Old status and results artifacts are also removed.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
tmpd = self.tmp_dir()
|
|
Packit Service |
a04d08 |
data_d = self.tmp_path('data', tmpd)
|
|
Packit Service |
a04d08 |
link_d = self.tmp_path('link', tmpd)
|
|
Packit Service |
a04d08 |
status_link = self.tmp_path('status.json', link_d)
|
|
Packit Service |
a04d08 |
# Write old artifacts which will be removed or updated.
|
|
Packit Service |
a04d08 |
for _dir in data_d, link_d:
|
|
Packit Service |
a04d08 |
test_helpers.populate_dir(
|
|
Packit Service |
a04d08 |
_dir, {'status.json': 'old', 'result.json': 'old'})
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
FakeArgs = namedtuple('FakeArgs', ['action', 'local', 'mode'])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def myaction(name, args):
|
|
Packit Service |
a04d08 |
# Return an error to watch status capture them
|
|
Packit Service |
a04d08 |
return 'SomeDatasource', ['an error']
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
myargs = FakeArgs(('ignored_name', myaction), True, 'bogusmode')
|
|
Packit Service |
a04d08 |
cli.status_wrapper('init', myargs, data_d, link_d)
|
|
Packit Service |
a04d08 |
# No errors reported in status
|
|
Packit Service |
a04d08 |
status_v1 = load_json(load_file(status_link))['v1']
|
|
Packit Service |
a04d08 |
self.assertEqual(['an error'], status_v1['init-local']['errors'])
|
|
Packit Service |
a04d08 |
self.assertEqual('SomeDatasource', status_v1['datasource'])
|
|
Packit Service |
a04d08 |
self.assertFalse(
|
|
Packit Service |
a04d08 |
os.path.exists(self.tmp_path('result.json', data_d)),
|
|
Packit Service |
a04d08 |
'unexpected result.json found')
|
|
Packit Service |
a04d08 |
self.assertFalse(
|
|
Packit Service |
a04d08 |
os.path.exists(self.tmp_path('result.json', link_d)),
|
|
Packit Service |
a04d08 |
'unexpected result.json link found')
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_no_arguments_shows_usage(self):
|
|
Packit Service |
a04d08 |
exit_code = self._call_main()
|
|
Packit Service |
a04d08 |
self.assertIn('usage: cloud-init', self.stderr.getvalue())
|
|
Packit Service |
a04d08 |
self.assertEqual(2, exit_code)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_no_arguments_shows_error_message(self):
|
|
Packit Service |
a04d08 |
exit_code = self._call_main()
|
|
Packit Service |
a04d08 |
missing_subcommand_message = [
|
|
Packit Service |
a04d08 |
'too few arguments', # python2.7 msg
|
|
Packit Service |
a04d08 |
'the following arguments are required: subcommand' # python3 msg
|
|
Packit Service |
a04d08 |
]
|
|
Packit Service |
a04d08 |
error = self.stderr.getvalue()
|
|
Packit Service |
a04d08 |
matches = ([msg in error for msg in missing_subcommand_message])
|
|
Packit Service |
a04d08 |
self.assertTrue(
|
|
Packit Service |
a04d08 |
any(matches), 'Did not find error message for missing subcommand')
|
|
Packit Service |
a04d08 |
self.assertEqual(2, exit_code)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_all_subcommands_represented_in_help(self):
|
|
Packit Service |
a04d08 |
"""All known subparsers are represented in the cloud-int help doc."""
|
|
Packit Service |
a04d08 |
self._call_main()
|
|
Packit Service |
a04d08 |
error = self.stderr.getvalue()
|
|
Packit Service |
a04d08 |
expected_subcommands = ['analyze', 'clean', 'devel', 'dhclient-hook',
|
|
Packit Service |
a04d08 |
'features', 'init', 'modules', 'single']
|
|
Packit Service |
a04d08 |
for subcommand in expected_subcommands:
|
|
Packit Service |
a04d08 |
self.assertIn(subcommand, error)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch('cloudinit.cmd.main.status_wrapper')
|
|
Packit Service |
a04d08 |
def test_init_subcommand_parser(self, m_status_wrapper):
|
|
Packit Service |
a04d08 |
"""The subcommand 'init' calls status_wrapper passing init."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'init'])
|
|
Packit Service |
a04d08 |
(name, parseargs) = m_status_wrapper.call_args_list[0][0]
|
|
Packit Service |
a04d08 |
self.assertEqual('init', name)
|
|
Packit Service |
a04d08 |
self.assertEqual('init', parseargs.subcommand)
|
|
Packit Service |
a04d08 |
self.assertEqual('init', parseargs.action[0])
|
|
Packit Service |
a04d08 |
self.assertEqual('main_init', parseargs.action[1].__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch('cloudinit.cmd.main.status_wrapper')
|
|
Packit Service |
a04d08 |
def test_modules_subcommand_parser(self, m_status_wrapper):
|
|
Packit Service |
a04d08 |
"""The subcommand 'modules' calls status_wrapper passing modules."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'modules'])
|
|
Packit Service |
a04d08 |
(name, parseargs) = m_status_wrapper.call_args_list[0][0]
|
|
Packit Service |
a04d08 |
self.assertEqual('modules', name)
|
|
Packit Service |
a04d08 |
self.assertEqual('modules', parseargs.subcommand)
|
|
Packit Service |
a04d08 |
self.assertEqual('modules', parseargs.action[0])
|
|
Packit Service |
a04d08 |
self.assertEqual('main_modules', parseargs.action[1].__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_conditional_subcommands_from_entry_point_sys_argv(self):
|
|
Packit Service |
a04d08 |
"""Subcommands from entry-point are properly parsed from sys.argv."""
|
|
Packit Service |
751c4a |
stdout = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stdout=stdout)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
expected_errors = [
|
|
Packit Service |
a04d08 |
'usage: cloud-init analyze', 'usage: cloud-init clean',
|
|
Packit Service |
a04d08 |
'usage: cloud-init collect-logs', 'usage: cloud-init devel',
|
|
Packit Service |
a04d08 |
'usage: cloud-init status']
|
|
Packit Service |
a04d08 |
conditional_subcommands = [
|
|
Packit Service |
a04d08 |
'analyze', 'clean', 'collect-logs', 'devel', 'status']
|
|
Packit Service |
a04d08 |
# The cloud-init entrypoint calls main without passing sys_argv
|
|
Packit Service |
a04d08 |
for subcommand in conditional_subcommands:
|
|
Packit Service |
a04d08 |
with mock.patch('sys.argv', ['cloud-init', subcommand, '-h']):
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
cli.main()
|
|
Packit Service |
a04d08 |
except SystemExit as e:
|
|
Packit Service |
a04d08 |
self.assertEqual(0, e.code) # exit 2 on proper -h usage
|
|
Packit Service |
a04d08 |
for error_message in expected_errors:
|
|
Packit Service |
a04d08 |
self.assertIn(error_message, stdout.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_analyze_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init analyze calls the correct subparser."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'analyze'])
|
|
Packit Service |
a04d08 |
# These subcommands only valid for cloud-init analyze script
|
|
Packit Service |
a04d08 |
expected_subcommands = ['blame', 'show', 'dump']
|
|
Packit Service |
a04d08 |
error = self.stderr.getvalue()
|
|
Packit Service |
a04d08 |
for subcommand in expected_subcommands:
|
|
Packit Service |
a04d08 |
self.assertIn(subcommand, error)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_collect_logs_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init collect-logs calls the subparser."""
|
|
Packit Service |
a04d08 |
# Provide -h param to collect-logs to avoid having to mock behavior.
|
|
Packit Service |
751c4a |
stdout = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stdout=stdout)
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'collect-logs', '-h'])
|
|
Packit Service |
a04d08 |
self.assertIn('usage: cloud-init collect-log', stdout.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_clean_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init clean calls the subparser."""
|
|
Packit Service |
a04d08 |
# Provide -h param to clean to avoid having to mock behavior.
|
|
Packit Service |
751c4a |
stdout = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stdout=stdout)
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'clean', '-h'])
|
|
Packit Service |
a04d08 |
self.assertIn('usage: cloud-init clean', stdout.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_status_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init status calls the subparser."""
|
|
Packit Service |
a04d08 |
# Provide -h param to clean to avoid having to mock behavior.
|
|
Packit Service |
751c4a |
stdout = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stdout=stdout)
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'status', '-h'])
|
|
Packit Service |
a04d08 |
self.assertIn('usage: cloud-init status', stdout.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_devel_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init devel calls the correct subparser."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'devel'])
|
|
Packit Service |
a04d08 |
# These subcommands only valid for cloud-init schema script
|
|
Packit Service |
a04d08 |
expected_subcommands = ['schema']
|
|
Packit Service |
a04d08 |
error = self.stderr.getvalue()
|
|
Packit Service |
a04d08 |
for subcommand in expected_subcommands:
|
|
Packit Service |
a04d08 |
self.assertIn(subcommand, error)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_wb_devel_schema_subcommand_parser(self):
|
|
Packit Service |
a04d08 |
"""The subcommand cloud-init schema calls the correct subparser."""
|
|
Packit Service |
a04d08 |
exit_code = self._call_main(['cloud-init', 'devel', 'schema'])
|
|
Packit Service |
a04d08 |
self.assertEqual(1, exit_code)
|
|
Packit Service |
a04d08 |
# Known whitebox output from schema subcommand
|
|
Packit Service |
a04d08 |
self.assertEqual(
|
|
Packit Service |
751c4a |
'Expected either --config-file argument or --docs\n',
|
|
Packit Service |
a04d08 |
self.stderr.getvalue())
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def test_wb_devel_schema_subcommand_doc_content(self):
|
|
Packit Service |
a04d08 |
"""Validate that doc content is sane from known examples."""
|
|
Packit Service |
751c4a |
stdout = io.StringIO()
|
|
Packit Service |
a04d08 |
self.patchStdoutAndStderr(stdout=stdout)
|
|
Packit Service |
751c4a |
self._call_main(['cloud-init', 'devel', 'schema', '--docs', 'all'])
|
|
Packit Service |
a04d08 |
expected_doc_sections = [
|
|
Packit Service |
a04d08 |
'**Supported distros:** all',
|
|
Packit Service |
751c4a |
'**Supported distros:** alpine, centos, debian, fedora',
|
|
Packit Service |
a04d08 |
'**Config schema**:\n **resize_rootfs:** (true/false/noblock)',
|
|
Packit Service |
a04d08 |
'**Examples**::\n\n runcmd:\n - [ ls, -l, / ]\n'
|
|
Packit Service |
a04d08 |
]
|
|
Packit Service |
a04d08 |
stdout = stdout.getvalue()
|
|
Packit Service |
a04d08 |
for expected in expected_doc_sections:
|
|
Packit Service |
a04d08 |
self.assertIn(expected, stdout)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch('cloudinit.cmd.main.main_single')
|
|
Packit Service |
a04d08 |
def test_single_subcommand(self, m_main_single):
|
|
Packit Service |
a04d08 |
"""The subcommand 'single' calls main_single with valid args."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'single', '--name', 'cc_ntp'])
|
|
Packit Service |
a04d08 |
(name, parseargs) = m_main_single.call_args_list[0][0]
|
|
Packit Service |
a04d08 |
self.assertEqual('single', name)
|
|
Packit Service |
a04d08 |
self.assertEqual('single', parseargs.subcommand)
|
|
Packit Service |
a04d08 |
self.assertEqual('single', parseargs.action[0])
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.debug)
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.force)
|
|
Packit Service |
a04d08 |
self.assertIsNone(parseargs.frequency)
|
|
Packit Service |
a04d08 |
self.assertEqual('cc_ntp', parseargs.name)
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.report)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch('cloudinit.cmd.main.dhclient_hook.handle_args')
|
|
Packit Service |
a04d08 |
def test_dhclient_hook_subcommand(self, m_handle_args):
|
|
Packit Service |
a04d08 |
"""The subcommand 'dhclient-hook' calls dhclient_hook with args."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'dhclient-hook', 'up', 'eth0'])
|
|
Packit Service |
a04d08 |
(name, parseargs) = m_handle_args.call_args_list[0][0]
|
|
Packit Service |
a04d08 |
self.assertEqual('dhclient-hook', name)
|
|
Packit Service |
a04d08 |
self.assertEqual('dhclient-hook', parseargs.subcommand)
|
|
Packit Service |
a04d08 |
self.assertEqual('dhclient-hook', parseargs.action[0])
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.debug)
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.force)
|
|
Packit Service |
a04d08 |
self.assertEqual('up', parseargs.event)
|
|
Packit Service |
a04d08 |
self.assertEqual('eth0', parseargs.interface)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
@mock.patch('cloudinit.cmd.main.main_features')
|
|
Packit Service |
a04d08 |
def test_features_hook_subcommand(self, m_features):
|
|
Packit Service |
a04d08 |
"""The subcommand 'features' calls main_features with args."""
|
|
Packit Service |
a04d08 |
self._call_main(['cloud-init', 'features'])
|
|
Packit Service |
a04d08 |
(name, parseargs) = m_features.call_args_list[0][0]
|
|
Packit Service |
a04d08 |
self.assertEqual('features', name)
|
|
Packit Service |
a04d08 |
self.assertEqual('features', parseargs.subcommand)
|
|
Packit Service |
a04d08 |
self.assertEqual('features', parseargs.action[0])
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.debug)
|
|
Packit Service |
a04d08 |
self.assertFalse(parseargs.force)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# : ts=4 expandtab
|