From 55e06261aa86c87c7c059fbddc97cdbaae06e8dd Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 5 Jan 2018 15:11:17 -0500 Subject: [PATCH] Add python_exec kwarg to processutils.execute() This commit adds a new kwarg to the process_utils.execute() function to specify the python executable to use when launching python to check prlimits. This is necessary when processutils.execute() is called from inside an API server running with uwsgi. In this case sys.executable is uwsgi (because uwsgi links libpython.so and is actually the interpreter) This doesn't work with the execute() function because it assumes the cpython interpreter CLI is used for the arguments it uses to call the prlimits module. To workaround this and enable API servers that may run under uwsgi to use this those applications can simply pass in an executable to use. Longer term it might be better to migrate the prlimits usage to call multiprocessing instead of subprocessing python. But that would require a more significant rewrite of both processutils and prlimit to facilitate that. Change-Id: I0ae60f0b4cc3700c783f6018e837358f0e053a09 Closes-Bug: #1712463 --- oslo_concurrency/processutils.py | 7 ++++++- oslo_concurrency/tests/unit/test_processutils.py | 13 +++++++++++++ .../add-python-exec-kwarg-3a7a0c0849f9bb21.yaml | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-python-exec-kwarg-3a7a0c0849f9bb21.yaml diff --git a/oslo_concurrency/processutils.py b/oslo_concurrency/processutils.py index 744c849..8479a3a 100644 --- a/oslo_concurrency/processutils.py +++ b/oslo_concurrency/processutils.py @@ -262,6 +262,10 @@ def execute(*cmd, **kwargs): :param prlimit: Set resource limits on the child process. See below for a detailed description. :type prlimit: :class:`ProcessLimits` + :param python_exec: The python executable to use for enforcing + prlimits. If this is not set it will default to use + sys.executable. + :type python_exec: string :returns: (stdout, stderr) from process execution :raises: :class:`UnknownArgumentError` on receiving unknown arguments @@ -314,6 +318,7 @@ def execute(*cmd, **kwargs): on_completion = kwargs.pop('on_completion', None) preexec_fn = kwargs.pop('preexec_fn', None) prlimit = kwargs.pop('prlimit', None) + python_exec = kwargs.pop('python_exec', sys.executable) if isinstance(check_exit_code, bool): ignore_exit_code = not check_exit_code @@ -350,7 +355,7 @@ def execute(*cmd, **kwargs): _('Process resource limits are ignored as ' 'this feature is not supported on Windows.')) else: - args = [sys.executable, '-m', 'oslo_concurrency.prlimit'] + args = [python_exec, '-m', 'oslo_concurrency.prlimit'] args.extend(prlimit.prlimit_args()) args.append('--') args.extend(cmd) diff --git a/oslo_concurrency/tests/unit/test_processutils.py b/oslo_concurrency/tests/unit/test_processutils.py index 29b4c65..29576e5 100644 --- a/oslo_concurrency/tests/unit/test_processutils.py +++ b/oslo_concurrency/tests/unit/test_processutils.py @@ -968,3 +968,16 @@ class PrlimitTestCase(test_base.BaseTestCase): stderr=mock.ANY, close_fds=mock.ANY, preexec_fn=mock.ANY, shell=mock.ANY, cwd=mock.ANY, env=mock.ANY) + + @mock.patch.object(processutils.subprocess, 'Popen') + def test_python_exec(self, sub_mock): + mock_subprocess = mock.MagicMock() + mock_subprocess.communicate.return_value = (b'', b'') + sub_mock.return_value = mock_subprocess + args = ['/a/command'] + prlimit = self.limit_address_space() + + processutils.execute(*args, prlimit=prlimit, check_exit_code=False, + python_exec='/fake_path') + python_path = sub_mock.mock_calls[0][1][0][0] + self.assertEqual('/fake_path', python_path) diff --git a/releasenotes/notes/add-python-exec-kwarg-3a7a0c0849f9bb21.yaml b/releasenotes/notes/add-python-exec-kwarg-3a7a0c0849f9bb21.yaml new file mode 100644 index 0000000..852d3fc --- /dev/null +++ b/releasenotes/notes/add-python-exec-kwarg-3a7a0c0849f9bb21.yaml @@ -0,0 +1,5 @@ +--- +features: + - A new kwarg, ``python_exec`` is added to the execute() function in the + processutils module. This option is used to specify the path to the python + executable to use for prlimits enforcement.