From 86d2fc7f515f70a5117f00baf701a0bed6310b3e Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Thu, 8 Feb 2018 20:57:23 -0700 Subject: cli: fix cloud-init status to report running when before result.json Fix various corner cases for cloud-init status subcommand. Report 'runnning' under the following conditions: - No /run/cloud-init/result.json file exists - Any stage in status.json is unfinished - status.json reports a non-null stage it is in progress on LP: #1747965 --- cloudinit/cmd/tests/test_status.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'cloudinit/cmd/tests/test_status.py') diff --git a/cloudinit/cmd/tests/test_status.py b/cloudinit/cmd/tests/test_status.py index a7c0a91a..4a5a8c06 100644 --- a/cloudinit/cmd/tests/test_status.py +++ b/cloudinit/cmd/tests/test_status.py @@ -7,7 +7,7 @@ from textwrap import dedent from cloudinit.atomic_helper import write_json from cloudinit.cmd import status -from cloudinit.util import write_file +from cloudinit.util import ensure_file from cloudinit.tests.helpers import CiTestCase, wrap_and_call, mock mypaths = namedtuple('MyPaths', 'run_dir') @@ -36,7 +36,7 @@ class TestStatus(CiTestCase): def test__is_cloudinit_disabled_false_on_sysvinit(self): '''When not in an environment using systemd, return False.''' - write_file(self.disable_file, '') # Create the ignored disable file + ensure_file(self.disable_file) # Create the ignored disable file (is_disabled, reason) = wrap_and_call( 'cloudinit.cmd.status', {'uses_systemd': False}, @@ -47,7 +47,7 @@ class TestStatus(CiTestCase): def test__is_cloudinit_disabled_true_on_disable_file(self): '''When using systemd and disable_file is present return disabled.''' - write_file(self.disable_file, '') # Create observed disable file + ensure_file(self.disable_file) # Create observed disable file (is_disabled, reason) = wrap_and_call( 'cloudinit.cmd.status', {'uses_systemd': True}, @@ -58,7 +58,7 @@ class TestStatus(CiTestCase): def test__is_cloudinit_disabled_false_on_kernel_cmdline_enable(self): '''Not disabled when using systemd and enabled via commandline.''' - write_file(self.disable_file, '') # Create ignored disable file + ensure_file(self.disable_file) # Create ignored disable file (is_disabled, reason) = wrap_and_call( 'cloudinit.cmd.status', {'uses_systemd': True, @@ -96,7 +96,7 @@ class TestStatus(CiTestCase): def test__is_cloudinit_disabled_false_when_enabled_in_systemd(self): '''Report enabled when systemd generator creates the enabled file.''' enabled_file = os.path.join(self.paths.run_dir, 'enabled') - write_file(enabled_file, '') + ensure_file(enabled_file) (is_disabled, reason) = wrap_and_call( 'cloudinit.cmd.status', {'uses_systemd': True, @@ -149,8 +149,25 @@ class TestStatus(CiTestCase): ''') self.assertEqual(expected, m_stdout.getvalue()) + def test_status_returns_running_on_no_results_json(self): + '''Report running when status.json exists but result.json does not.''' + result_file = self.tmp_path('result.json', self.new_root) + write_json(self.status_file, {}) + self.assertFalse( + os.path.exists(result_file), 'Unexpected result.json found') + cmdargs = myargs(long=False, wait=False) + with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout: + retcode = wrap_and_call( + 'cloudinit.cmd.status', + {'_is_cloudinit_disabled': (False, ''), + 'Init': {'side_effect': self.init_class}}, + status.handle_status_args, 'ignored', cmdargs) + self.assertEqual(0, retcode) + self.assertEqual('status: running\n', m_stdout.getvalue()) + def test_status_returns_running(self): '''Report running when status exists with an unfinished stage.''' + ensure_file(self.tmp_path('result.json', self.new_root)) write_json(self.status_file, {'v1': {'init': {'start': 1, 'finished': None}}}) cmdargs = myargs(long=False, wait=False) @@ -164,10 +181,11 @@ class TestStatus(CiTestCase): self.assertEqual('status: running\n', m_stdout.getvalue()) def test_status_returns_done(self): - '''Reports done when stage is None and all stages are finished.''' + '''Report done results.json exists no stages are unfinished.''' + ensure_file(self.tmp_path('result.json', self.new_root)) write_json( self.status_file, - {'v1': {'stage': None, + {'v1': {'stage': None, # No current stage running 'datasource': ( 'DataSourceNoCloud [seed=/var/.../seed/nocloud-net]' '[dsmode=net]'), @@ -187,6 +205,7 @@ class TestStatus(CiTestCase): def test_status_returns_done_long(self): '''Long format of done status includes datasource info.''' + ensure_file(self.tmp_path('result.json', self.new_root)) write_json( self.status_file, {'v1': {'stage': None, @@ -303,6 +322,8 @@ class TestStatus(CiTestCase): write_json(self.status_file, running_json) elif self.sleep_calls == 3: write_json(self.status_file, done_json) + result_file = self.tmp_path('result.json', self.new_root) + ensure_file(result_file) cmdargs = myargs(long=False, wait=True) with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout: -- cgit v1.2.3 From 754f54037aca0f604b8b57ab71b30dad5e5066cf Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Feb 2018 13:54:50 -0700 Subject: tests: run nosetests in cloudinit/ directory, fix py26 fallout. When we moved some tests to live under cloudinit/ we inadvertantly failed to change all things that would run nose to include that directory. This changes all the 'nose' invocations to consistently run with tests/unittests and cloudinit/. Also, it works around, more correctly this time, a python2.6-ism with the following code: with assertRaises(SystemExit) as cm: sys.exit(2) --- cloudinit/cmd/tests/test_clean.py | 3 ++- cloudinit/cmd/tests/test_status.py | 3 ++- cloudinit/tests/helpers.py | 20 +++++++++----------- tests/unittests/test_handler/test_schema.py | 12 +++++++----- tools/run-centos | 3 ++- tox.ini | 6 +++--- 6 files changed, 25 insertions(+), 22 deletions(-) (limited to 'cloudinit/cmd/tests/test_status.py') diff --git a/cloudinit/cmd/tests/test_clean.py b/cloudinit/cmd/tests/test_clean.py index 6713af4f..5a3ec3bf 100644 --- a/cloudinit/cmd/tests/test_clean.py +++ b/cloudinit/cmd/tests/test_clean.py @@ -165,10 +165,11 @@ class TestClean(CiTestCase): wrap_and_call( 'cloudinit.cmd.clean', {'Init': {'side_effect': self.init_class}, + 'sys.exit': {'side_effect': self.sys_exit}, 'sys.argv': {'new': ['clean', '--logs']}}, clean.main) - self.assertRaisesCodeEqual(0, context_manager.exception.code) + self.assertEqual(0, context_manager.exception.code) self.assertFalse( os.path.exists(self.log1), 'Unexpected log {0}'.format(self.log1)) diff --git a/cloudinit/cmd/tests/test_status.py b/cloudinit/cmd/tests/test_status.py index 4a5a8c06..37a89936 100644 --- a/cloudinit/cmd/tests/test_status.py +++ b/cloudinit/cmd/tests/test_status.py @@ -380,10 +380,11 @@ class TestStatus(CiTestCase): wrap_and_call( 'cloudinit.cmd.status', {'sys.argv': {'new': ['status']}, + 'sys.exit': {'side_effect': self.sys_exit}, '_is_cloudinit_disabled': (False, ''), 'Init': {'side_effect': self.init_class}}, status.main) - self.assertRaisesCodeEqual(0, context_manager.exception.code) + self.assertEqual(0, context_manager.exception.code) self.assertEqual('status: running\n', m_stdout.getvalue()) # vi: ts=4 expandtab syntax=python diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py index 0080c729..41d9a8ee 100644 --- a/cloudinit/tests/helpers.py +++ b/cloudinit/tests/helpers.py @@ -173,17 +173,15 @@ class CiTestCase(TestCase): dir = self.tmp_dir() return os.path.normpath(os.path.abspath(os.path.join(dir, path))) - def assertRaisesCodeEqual(self, expected, found): - """Handle centos6 having different context manager for assertRaises. - with assertRaises(Exception) as e: - raise Exception("BOO") - - centos6 will have e.exception as an integer. - anything nwere will have it as something with a '.code'""" - if isinstance(found, int): - self.assertEqual(expected, found) - else: - self.assertEqual(expected, found.code) + def sys_exit(self, code): + """Provide a wrapper around sys.exit for python 2.6 + + In 2.6, this code would produce 'cm.exception' with value int(2) + rather than the SystemExit that was raised by sys.exit(2). + with assertRaises(SystemExit) as cm: + sys.exit(2) + """ + raise SystemExit(code) class ResourceUsingTestCase(CiTestCase): diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py index 648573f6..df67a0e0 100644 --- a/tests/unittests/test_handler/test_schema.py +++ b/tests/unittests/test_handler/test_schema.py @@ -336,11 +336,13 @@ class MainTest(CiTestCase): def test_main_missing_args(self): """Main exits non-zero and reports an error on missing parameters.""" - with mock.patch('sys.argv', ['mycmd']): - with mock.patch('sys.stderr', new_callable=StringIO) as m_stderr: - with self.assertRaises(SystemExit) as context_manager: - main() - self.assertEqual('1', str(context_manager.exception)) + with mock.patch('sys.exit', side_effect=self.sys_exit): + with mock.patch('sys.argv', ['mycmd']): + with mock.patch('sys.stderr', new_callable=StringIO) as \ + m_stderr: + with self.assertRaises(SystemExit) as context_manager: + main() + self.assertEqual(1, context_manager.exception.code) self.assertEqual( 'Expected either --config-file argument or --doc\n', m_stderr.getvalue()) diff --git a/tools/run-centos b/tools/run-centos index 6ac6c11f..cb241ee5 100755 --- a/tools/run-centos +++ b/tools/run-centos @@ -313,7 +313,8 @@ main() { if [ -n "$unittest" ]; then debug 1 "running unit tests." - inside_as_cd "$name" "$user" "$cdir" nosetests tests/unittests || + inside_as_cd "$name" "$user" "$cdir" \ + nosetests tests/unittests cloudinit || { errorrc "nosetests failed."; errors=$(($errors+1)); } fi diff --git a/tox.ini b/tox.ini index bb74853f..1f990af4 100644 --- a/tox.ini +++ b/tox.ini @@ -45,7 +45,7 @@ deps = -r{toxinidir}/test-requirements.txt [testenv:py26] deps = -r{toxinidir}/test-requirements.txt -commands = nosetests {posargs:tests/unittests} +commands = nosetests {posargs:tests/unittests cloudinit} setenv = LC_ALL = C @@ -83,7 +83,7 @@ deps = [testenv:centos6] basepython = python2.6 -commands = nosetests {posargs:tests/unittests} +commands = nosetests {posargs:tests/unittests cloudinit} deps = # requirements argparse==1.2.1 @@ -98,7 +98,7 @@ deps = [testenv:opensusel42] basepython = python2.7 -commands = nosetests {posargs:tests/unittests} +commands = nosetests {posargs:tests/unittests cloudinit} deps = # requirements argparse==1.3.0 -- cgit v1.2.3