diff --git a/azurelinuxagent/common/utils/cryptutil.py b/azurelinuxagent/common/utils/cryptutil.py index 971d59f6fa..9cc81e5a68 100644 --- a/azurelinuxagent/common/utils/cryptutil.py +++ b/azurelinuxagent/common/utils/cryptutil.py @@ -53,24 +53,24 @@ def get_pubkey_from_prv(self, file_name): if not os.path.exists(file_name): raise IOError(errno.ENOENT, "File not found", file_name) else: - cmd = "{0} pkey -in {1} -pubout 2>/dev/null".format(self.openssl_cmd, file_name) - pub = shellutil.run_get_output(cmd)[1] + cmd = "{0} pkey -in {1} -pubout".format(self.openssl_cmd, file_name) + pub = shellutil.run_command(cmd.split(" ")) return pub def get_pubkey_from_crt(self, file_name): if not os.path.exists(file_name): raise IOError(errno.ENOENT, "File not found", file_name) else: - cmd = "{0} x509 -in {1} -pubkey -noout 2>/dev/null".format(self.openssl_cmd, file_name) - pub = shellutil.run_get_output(cmd)[1] + cmd = "{0} x509 -in {1} -pubkey -noout".format(self.openssl_cmd, file_name) + pub = shellutil.run_command(cmd.split(" ")) return pub def get_thumbprint_from_crt(self, file_name): if not os.path.exists(file_name): raise IOError(errno.ENOENT, "File not found", file_name) else: - cmd = "{0} x509 -in {1} -fingerprint -noout 2>/dev/null".format(self.openssl_cmd, file_name) - thumbprint = shellutil.run_get_output(cmd)[1] + cmd = "{0} x509 -in {1} -fingerprint -noout".format(self.openssl_cmd, file_name) + thumbprint = shellutil.run_command(cmd.split(" ")) thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper() return thumbprint diff --git a/azurelinuxagent/common/utils/shellutil.py b/azurelinuxagent/common/utils/shellutil.py index 9bab3eb8f3..01e78dd76d 100644 --- a/azurelinuxagent/common/utils/shellutil.py +++ b/azurelinuxagent/common/utils/shellutil.py @@ -110,6 +110,34 @@ def run_get_output(cmd, chk_err=True, log_cmd=True, expected_errors=[]): return 0, output +def _encode_command_output(output): + return ustr(output, encoding='utf-8', errors="backslashreplace") + + +def run_command(command): + """ + Wrapper for subprocess.Popen. + Executes the given command and returns its stdout. Logs any errors executing the command and raises an exception. + """ + try: + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) + stdout, stderr = process.communicate() + retcode = process.returncode + + if retcode: + logger.error(u"Command: [{0}], return code: [{1}], " + u"stdout: [{2}] stderr: [{3}]".format(command, + retcode, + _encode_command_output(stdout), + _encode_command_output(stderr))) + raise subprocess.CalledProcessError(retcode, command) + except Exception as e: + logger.error(u"Command [{0}] raised unexpected exception: [{1}]".format(command, ustr(e))) + raise + + return _encode_command_output(stdout) + + def quote(word_list): """ Quote a list or tuple of strings for Unix Shell as words, using the diff --git a/tests/utils/test_shell_util.py b/tests/utils/test_shell_util.py index 566247ff93..1b9bc82dc9 100644 --- a/tests/utils/test_shell_util.py +++ b/tests/utils/test_shell_util.py @@ -21,6 +21,7 @@ import unittest import os import azurelinuxagent.common.utils.shellutil as shellutil +import subprocess import test @@ -142,5 +143,40 @@ def test_it_should_log_unexpected_errors_as_errors(self): self.assertEquals(mock_logger.warn.call_count, 0) +class RunCommandTestCase(AgentTestCase): + def test_run_command_it_should_run_without_errors(self): + command = "echo 42".split(" ") + + with patch("azurelinuxagent.common.utils.shellutil.logger", autospec=True) as mock_logger: + ret = shellutil.run_command(command) + self.assertEquals(ret, "42\n") + self.assertEquals(mock_logger.error.call_count, 0) + + def test_run_command_it_should_raise_an_exception_file_not_found(self): + command = "ls nonexistent_file".split(" ") + expected_returncode = 2 + + with patch("azurelinuxagent.common.utils.shellutil.logger") as mock_logger: + with self.assertRaises(subprocess.CalledProcessError) as context_manager: + shellutil.run_command(command) + + ex = context_manager.exception + self.assertEquals(subprocess.CalledProcessError, type(ex)) + self.assertEquals((expected_returncode, ["ls", "nonexistent_file"]), ex.args) + + self.assertEquals(mock_logger.error.call_count, 2) + + logged_error_first = "Command: [{0}], return code: [{1}], " \ + "stdout: [] stderr: [{2}]".format(command, expected_returncode, + "ls: cannot access 'nonexistent_file': " + "No such file or directory\n") + self.assertEquals(mock_logger.error.call_args_list[0][0][0], logged_error_first) + + logged_error_second = "Command [{0}] raised unexpected exception: " \ + "[Command '{0}' returned non-zero exit status {1}.]".format(command, + expected_returncode) + self.assertEquals(mock_logger.error.call_args_list[1][0][0], logged_error_second) + + if __name__ == '__main__': unittest.main()