From 525638357f5ebffc6ca10799f1775827c3325bc9 Mon Sep 17 00:00:00 2001 From: Michael Pimmer Date: Wed, 24 Oct 2018 00:45:13 +0000 Subject: [PATCH] Actions: use local plinth in development mode Reviewed-by: James Valleroy --- actions/test_path | 24 +++++++++++++++++++++++ plinth/actions.py | 38 ++++++++++++++++++++++++++++++++---- plinth/tests/test_actions.py | 28 ++++++++++++++++++++++---- 3 files changed, 82 insertions(+), 8 deletions(-) create mode 100755 actions/test_path diff --git a/actions/test_path b/actions/test_path new file mode 100755 index 000000000..97c2f7b24 --- /dev/null +++ b/actions/test_path @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +# -*- mode: python -*- +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Helper to test whether action scripts use the correct PYTHONPATH. +""" + +import plinth +print(plinth.__file__) diff --git a/plinth/actions.py b/plinth/actions.py index 461f83823..8d42d84e0 100644 --- a/plinth/actions.py +++ b/plinth/actions.py @@ -168,17 +168,33 @@ def _run(action, options=None, input=None, run_in_background=False, cmd += list(options) # No escaping necessary # Contract 1: commands can run via sudo. + sudo_call = [] if run_as_root: - cmd = ['sudo', '-n'] + cmd + sudo_call = ['sudo', '-n'] elif become_user: - cmd = ['sudo', '-n', '-u', become_user] + cmd + sudo_call = ['sudo', '-n', '-u', become_user] + if cfg.develop and sudo_call: + # Passing 'env' does not work with sudo, so append the PYTHONPATH + # as part of the command + pythonpath = _get_local_pythonpath() + sudo_call += ["PYTHONPATH=%s" % pythonpath] + if sudo_call: + cmd = sudo_call + cmd LOGGER.info('Executing command - %s', cmd) # Contract 3C: don't interpret shell escape sequences. # Contract 5 (and 6-ish). - proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=False) + kwargs = { + "stdin": subprocess.PIPE, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "shell": False, + } + if cfg.develop: + # In development mode pass on local pythonpath to access Plinth + kwargs['env'] = {'PYTHONPATH': _get_local_pythonpath()} + proc = subprocess.Popen(cmd, **kwargs) if not run_in_background: output, error = proc.communicate(input=input) @@ -192,3 +208,17 @@ def _run(action, options=None, input=None, run_in_background=False, return output else: return proc + +def _get_local_pythonpath(): + """Use local plinth folder in pythonpath instead of system plinth""" + pythonpath = cfg.root + try: + current_pythonpath = os.environ['PYTHONPATH'] + except KeyError: + pass + else: + current_pythonpath = current_pythonpath.strip(os.path.pathsep) + if current_pythonpath: + pythonpath += os.path.pathsep.join([pythonpath, + current_pythonpath]) + return pythonpath diff --git a/plinth/tests/test_actions.py b/plinth/tests/test_actions.py index 67b01f579..817d0c13e 100644 --- a/plinth/tests/test_actions.py +++ b/plinth/tests/test_actions.py @@ -44,11 +44,11 @@ class TestActions(unittest.TestCase): """Initial setup for all the classes.""" cls.action_directory = tempfile.TemporaryDirectory() cfg.actions_dir = cls.action_directory.name + actions_dir = os.path.join(os.path.dirname(__file__), '..', '..', + 'actions') - shutil.copy( - os.path.join( - os.path.dirname(__file__), '..', '..', 'actions', 'packages'), - cfg.actions_dir) + shutil.copy(os.path.join(actions_dir, 'packages'), cfg.actions_dir) + shutil.copy(os.path.join(actions_dir, 'test_path'), cfg.actions_dir) shutil.copy('/bin/echo', cfg.actions_dir) shutil.copy('/usr/bin/id', cfg.actions_dir) @@ -166,3 +166,23 @@ class TestActions(unittest.TestCase): # An ActionError is raised in this case. with self.assertRaises(ActionError): superuser_run('packages', ['is-package-manager-busy']) + + @unittest.skipUnless(euid == 0, 'Needs to be root') + def test_action_path(self): + """Test that in development mode, python action scripts get the + correct PYTHONPATH""" + try: + cfg.develop = True + self._clear_env() + plinth_path = run('test_path').strip() + su_plinth_path = superuser_run('test_path').strip() + self.assertTrue(plinth_path.startswith(cfg.root)) + self.assertEquals(plinth_path, su_plinth_path) + finally: + cfg.develop = False + + def _clear_env(self): + try: + del os.environ['PYTHONPATH'] + except KeyError: + pass