From 4d0e468056f80ebabd15a1a40ce24db9e7166aa8 Mon Sep 17 00:00:00 2001 From: Jozef Mikovic Date: Wed, 3 Apr 2019 13:37:02 +0200 Subject: [PATCH] modify _call to allow env variable injection --- leapp/libraries/stdlib/__init__.py | 8 ++++---- leapp/libraries/stdlib/call.py | 11 +++++++++-- tests/scripts/test_stdlib_call.py | 7 +++++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/leapp/libraries/stdlib/__init__.py b/leapp/libraries/stdlib/__init__.py index ca766914e..37854d0a4 100644 --- a/leapp/libraries/stdlib/__init__.py +++ b/leapp/libraries/stdlib/__init__.py @@ -119,7 +119,7 @@ def _logging_handler(fd_info, buffer): __write_raw(fd_info, buffer) -def run(args, split=False, callback_raw=_logging_handler): +def run(args, split=False, callback_raw=_logging_handler, env=None): """ Run a command and return its result as a dict. @@ -138,8 +138,8 @@ def run(args, split=False, callback_raw=_logging_handler): _id = str(uuid.uuid4()) result = None try: - create_audit_entry('process-start', {'id': _id, 'parameters': args}) - result = _call(args, callback_raw=callback_raw) + create_audit_entry('process-start', {'id': _id, 'parameters': args, 'env': env}) + result = _call(args, callback_raw=callback_raw, env=env) if result['exit_code'] != 0: raise CalledProcessError( message="A Leapp Command Error occurred. ", @@ -153,6 +153,6 @@ def run(args, split=False, callback_raw=_logging_handler): }) finally: create_audit_entry('process-end', _id) - create_audit_entry('process-result', {'id': _id, 'parameters': args, 'result': result}) + create_audit_entry('process-result', {'id': _id, 'parameters': args, 'result': result, 'env': env}) api.current_logger().debug('External command is finished: [%s]', ' '.join(args)) return result diff --git a/leapp/libraries/stdlib/call.py b/leapp/libraries/stdlib/call.py index 24c214505..bdb650bde 100644 --- a/leapp/libraries/stdlib/call.py +++ b/leapp/libraries/stdlib/call.py @@ -82,7 +82,7 @@ def _multiplex(ep, read_fds, callback_raw, callback_linebuffered, # FIXME: Issue #488 def _call(command, callback_raw=lambda fd, value: None, callback_linebuffered=lambda fd, value: None, - encoding='utf-8', poll_timeout=1, read_buffer_size=80, stdin=None): + encoding='utf-8', poll_timeout=1, read_buffer_size=80, stdin=None, env=None): """ :param command: The command to execute :type command: list, tuple @@ -99,6 +99,8 @@ def _call(command, callback_raw=lambda fd, value: None, callback_linebuffered=la :type callback_linebuffered: (fd, buffer) -> None :param stdin: String or a file descriptor that will be written to stdin of the child process :type stdin: int, str + :param env: Environment variables to use for execution of the command + :type env: dict :return: {'stdout' : stdout, 'stderr': stderr, 'signal': signal, 'exit_code': exit_code, 'pid': pid} :rtype: dict """ @@ -113,6 +115,11 @@ def _call(command, callback_raw=lambda fd, value: None, callback_linebuffered=la if not isinstance(read_buffer_size, int) or isinstance(read_buffer_size, bool) or read_buffer_size <= 0: raise ValueError('read_buffer_size parameter has to be integer greater than zero') + environ = os.environ + if env: + if not isinstance(env, dict): + raise TypeError('env parameter has to be a dictionary') + environ.update(env) # Create a separate pipe for stdout/stderr # # The parent process is going to use the read-end of the pipes for reading child's @@ -201,4 +208,4 @@ def _call(command, callback_raw=lambda fd, value: None, callback_linebuffered=la os.close(stderr) os.dup2(wstdout, STDOUT) os.dup2(wstderr, STDERR) - os.execlp(command[0], *command) + os.execvpe(command[0], command, env=environ) diff --git a/tests/scripts/test_stdlib_call.py b/tests/scripts/test_stdlib_call.py index 34fb32fb4..efe7ad319 100644 --- a/tests/scripts/test_stdlib_call.py +++ b/tests/scripts/test_stdlib_call.py @@ -69,6 +69,13 @@ def test_output_2(): assert ret['signal'] == 0 +def test_env_injection(): + ret = _call(('bash', '-c', 'echo $TEST'), env={'TEST': 'SUCCESS'}) + assert isinstance(ret['exit_code'], int) + assert ret['exit_code'] == 0 + assert ret['stdout'] == 'SUCCESS\n' + + @pytest.mark.parametrize('p', _CALLBACKS) def test_callability_check(p): with pytest.raises(TypeError):