Wesley Wiedenmeier has proposed merging ~wesley-wiedenmeier/cloud-init:error-output into cloud-init:master.
Requested reviews: cloud init development team (cloud-init-dev) For more details, see: https://code.launchpad.net/~wesley-wiedenmeier/cloud-init/+git/cloud-init/+merge/306731 Improve formatting for util.ProcessExecutionError and properly display multi line error messages. Add unittests for util.ProcessExecutionError. old error formatting: http://paste.ubuntu.com/23232468/ new error formatting: http://paste.ubuntu.com/23232463/ -- Your team cloud init development team is requested to review the proposed merge of ~wesley-wiedenmeier/cloud-init:error-output into cloud-init:master.
diff --git a/cloudinit/util.py b/cloudinit/util.py index eb3e589..b9c88c9 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -235,15 +235,16 @@ class ProcessExecutionError(IOError): 'Command: %(cmd)s\n' 'Exit code: %(exit_code)s\n' 'Reason: %(reason)s\n' - 'Stdout: %(stdout)r\n' - 'Stderr: %(stderr)r') + 'Stdout: %(stdout)s\n' + 'Stderr: %(stderr)s') + empty_attr = '-' def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None, description=None, reason=None, errno=None): if not cmd: - self.cmd = '-' + self.cmd = self.empty_attr else: self.cmd = cmd @@ -253,24 +254,24 @@ class ProcessExecutionError(IOError): self.description = description if not isinstance(exit_code, six.integer_types): - self.exit_code = '-' + self.exit_code = self.empty_attr else: self.exit_code = exit_code if not stderr: - self.stderr = '' + self.stderr = self.empty_attr else: - self.stderr = stderr + self.stderr = self._indent_text(stderr) if not stdout: - self.stdout = '' + self.stdout = self.empty_attr else: - self.stdout = stdout + self.stdout = self._indent_text(stdout) if reason: self.reason = reason else: - self.reason = '-' + self.reason = self.empty_attr self.errno = errno message = self.MESSAGE_TMPL % { @@ -286,6 +287,20 @@ class ProcessExecutionError(IOError): if not hasattr(self, 'message'): self.message = message + def _indent_text(self, text, indent_level=8): + """ + indent text on all but the first line, allowing for easy to read output + """ + # if we are running on python3, then we need to make sure that this is + # a str, not bytes. on python2, this is not so important as bytes is + # basically just str + if six.PY3: + if isinstance(text, bytes): + text = text.decode() + # remove any newlines at end of text first to prevent unneeded blank + # line in output + return text.rstrip('\n').replace('\n', '\n' + ' ' * indent_level) + class SeLinuxGuard(object): def __init__(self, path, recursive=False): diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py index fc6b9d4..f8d416c 100644 --- a/tests/unittests/test_util.py +++ b/tests/unittests/test_util.py @@ -604,11 +604,70 @@ class TestSubp(helpers.TestCase): util.target_path("/target/", "///my/path/")) +<<<<<<< tests/unittests/test_util.py class TestEncode(helpers.TestCase): """Test the encoding functions""" def test_decode_binary_plain_text_with_hex(self): blob = 'BOOTABLE_FLAG=\x80init=/bin/systemd' text = util.decode_binary(blob) self.assertEqual(text, blob) +======= +class TestProcessExecutionError(helpers.TestCase): + + template = ('{description}\n' + 'Command: {cmd}\n' + 'Exit code: {exit_code}\n' + 'Reason: {reason}\n' + 'Stdout: {stdout}\n' + 'Stderr: {stderr}') + empty_attr = '-' + empty_description = 'Unexpected error while running command.' + + def test_pexec_error_type(self): + self.assertIsInstance(util.ProcessExecutionError(), IOError) + + def test_pexec_error_empty_msgs(self): + error = util.ProcessExecutionError() + self.assertTrue(all(attr == self.empty_attr for attr in + (error.stderr, error.stdout, error.reason))) + self.assertEqual(error.description, self.empty_description) + self.assertEqual(error.message, self.template.format( + description=self.empty_description, exit_code=self.empty_attr, + reason=self.empty_attr, stdout=self.empty_attr, + stderr=self.empty_attr, cmd=self.empty_attr)) + + def test_pexec_error_single_line_msgs(self): + stdout_msg = 'out out' + stderr_msg = 'error error' + cmd = 'test command' + exit_code = 3 + error = util.ProcessExecutionError( + stdout=stdout_msg, stderr=stderr_msg, exit_code=3, cmd=cmd) + self.assertEqual(error.message, self.template.format( + description=self.empty_description, stdout=stdout_msg, + stderr=stderr_msg, exit_code=str(exit_code), + reason=self.empty_attr, cmd=cmd)) + + def test_pexec_error_multi_line_msgs(self): + stdout_msg = 'multi\nline\noutput message' + stderr_msg = 'multi\nline\nerror message\n\n\n' + error = util.ProcessExecutionError( + stdout=stdout_msg, stderr=stderr_msg) + self.assertEqual( + error.message, + '\n'.join(( + '{description}', + 'Command: {empty_attr}', + 'Exit code: {empty_attr}', + 'Reason: {empty_attr}', + 'Stdout: multi', + ' line', + ' output message', + 'Stderr: multi', + ' line', + ' error message', + )).format(description=self.empty_description, + empty_attr=self.empty_attr)) +>>>>>>> tests/unittests/test_util.py # vi: ts=4 expandtab
_______________________________________________ Mailing list: https://launchpad.net/~cloud-init-dev Post to : cloud-init-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~cloud-init-dev More help : https://help.launchpad.net/ListHelp