Commit 2b6ae500 authored by Jérome Perrin's avatar Jérome Perrin

Ignore user site packages when running buildout

I had a Theia instance where I installed buildout with `pip install -e --user /path/to/checkout` and later deleted that `/path/to/checkout` and this made it impossible to run instance buildout, failing with import error when importing zc.buildout  in buildout script.

This is because python load user site packages by default. I believe we don't want this, a broken user site package should not prevent slapos from running buildout.

These changes are about running buildout with `PYTHONNOUSERSITE` set, so that python ignores user site packages when running buildout.

references: 

 * https://docs.python.org/3/using/cmdline.html#envvar-PYTHONNOUSERSITE
 * https://docs.python.org/3/library/site.html#site.ENABLE_USER_SITE

See merge request !266
parents 261072c7 8c1a4892
...@@ -40,7 +40,6 @@ import logging ...@@ -40,7 +40,6 @@ import logging
import psutil import psutil
import time import time
import six
from slapos.grid.exception import BuildoutFailedError, WrongPermissionError from slapos.grid.exception import BuildoutFailedError, WrongPermissionError
...@@ -57,8 +56,6 @@ PYTHON_ENVIRONMENT_REMOVE_LIST = [ ...@@ -57,8 +56,6 @@ PYTHON_ENVIRONMENT_REMOVE_LIST = [
'PYTHONDEBUG', 'PYTHONDEBUG',
'PYTHONDONTWRITEBYTECODE', 'PYTHONDONTWRITEBYTECODE',
'PYTHONINSPECT', 'PYTHONINSPECT',
'PYTHONNOUSERSITE',
'PYTHONNOUSERSITE',
'PYTHONUNBUFFERED', 'PYTHONUNBUFFERED',
'PYTHONVERBOSE', 'PYTHONVERBOSE',
] ]
...@@ -150,10 +147,11 @@ def getCleanEnvironment(logger, home_path='/tmp'): ...@@ -150,10 +147,11 @@ def getCleanEnvironment(logger, home_path='/tmp'):
if old is not None: if old is not None:
removed_env.append(k) removed_env.append(k)
changed_env['HOME'] = env['HOME'] = home_path changed_env['HOME'] = env['HOME'] = home_path
for k in sorted(six.iterkeys(changed_env)): changed_env['PYTHONNOUSERSITE'] = env['PYTHONNOUSERSITE'] = 'true'
logger.debug('Overridden %s = %r' % (k, changed_env[k])) for k, v in sorted(changed_env.items()):
logger.debug('Overridden %s = %r', k, v)
if removed_env: if removed_env:
logger.debug('Removed from environment: %s' % ', '.join(sorted(removed_env))) logger.debug('Removed from environment: %s', ', '.join(sorted(removed_env)))
return env return env
......
...@@ -283,6 +283,49 @@ class TestBasicSlapgridCP(BasicMixin, unittest.TestCase): ...@@ -283,6 +283,49 @@ class TestBasicSlapgridCP(BasicMixin, unittest.TestCase):
os.mkdir(self.instance_root) os.mkdir(self.instance_root)
self.assertRaises(ConnectionError, self.grid.processComputerPartitionList) self.assertRaises(ConnectionError, self.grid.processComputerPartitionList)
def test_environment_variable_HOME(self):
# When running instance, $HOME is set to the partition path
computer = ComputerForTest(self.software_root, self.instance_root)
partition = computer.instance_list[0]
partition.requested_state = 'started'
partition.software.setBuildout('#!/bin/sh\n echo $HOME > env_HOME')
with httmock.HTTMock(computer.request_handler):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
with open(os.path.join(partition.partition_path, 'env_HOME')) as f:
self.assertEqual(f.read().strip(), partition.partition_path)
def test_no_user_site_packages(self):
# When running instance buildout, python's user site packages are ignored
computer = ComputerForTest(self.software_root, self.instance_root)
partition = computer.instance_list[0]
partition.requested_state = 'started'
# Make a broken user site package in this partition home, that will error
# as soon as site is initialized, making python unusable. While this is
# a bit unrealistic, real life problems were observed after installing a
# broken buildout with `pip install --editable --user`, because user site
# packages have priority over sys.path assignments in buildout generated
# scripts.
home_site_packages_dir = os.path.join(
partition.partition_path,
'.local',
'lib',
'python{version[0]}.{version[1]}'.format(version=sys.version_info),
'site-packages',
)
os.makedirs(home_site_packages_dir)
with open(os.path.join(home_site_packages_dir, 'dummy-nspkg.pth'),
'w') as f:
f.write('import sys; raise SystemExit("ahaha")')
partition.software.setBuildout(textwrap.dedent('''\
#!{sys.executable}
print("ok")
'''.format(sys=sys)))
with httmock.HTTMock(computer.request_handler):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
class MasterMixin(BasicMixin): class MasterMixin(BasicMixin):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment