bc074906732f69ab0cc04d49b19558d4f6df82be
[osm/riftware.git] /
1 import os
2 import sys
3 import shutil
4 from glob import glob
5 from subprocess import check_call
6
7
8 def bootstrap_charm_deps():
9     """
10     Set up the base charm dependencies so that the reactive system can run.
11     """
12     venv = os.path.abspath('../.venv')
13     vbin = os.path.join(venv, 'bin')
14     vpip = os.path.join(vbin, 'pip')
15     vpy = os.path.join(vbin, 'python')
16     if os.path.exists('wheelhouse/.bootstrapped'):
17         from charms import layer
18         cfg = layer.options('basic')
19         if cfg.get('use_venv') and '.venv' not in sys.executable:
20             # activate the venv
21             os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
22             reload_interpreter(vpy)
23         return
24     # bootstrap wheelhouse
25     if os.path.exists('wheelhouse'):
26         apt_install(['python3-pip', 'python3-yaml'])
27         from charms import layer
28         cfg = layer.options('basic')
29         # include packages defined in layer.yaml
30         apt_install(cfg.get('packages', []))
31         # if we're using a venv, set it up
32         if cfg.get('use_venv'):
33             apt_install(['python-virtualenv'])
34             cmd = ['virtualenv', '--python=python3', venv]
35             if cfg.get('include_system_packages'):
36                 cmd.append('--system-site-packages')
37             check_call(cmd)
38             os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
39             pip = vpip
40         else:
41             pip = 'pip3'
42             # save a copy of system pip to prevent `pip3 install -U pip` from changing it
43             if os.path.exists('/usr/bin/pip'):
44                 shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
45         # need newer pip, to fix spurious Double Requirement error https://github.com/pypa/pip/issues/56
46         check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip'])
47         # install the rest of the wheelhouse deps
48         check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*'))
49         if not cfg.get('use_venv'):
50             # restore system pip to prevent `pip3 install -U pip` from changing it
51             if os.path.exists('/usr/bin/pip.save'):
52                 shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
53                 os.remove('/usr/bin/pip.save')
54         # flag us as having already bootstrapped so we don't do it again
55         open('wheelhouse/.bootstrapped', 'w').close()
56         # Ensure that the newly bootstrapped libs are available.
57         # Note: this only seems to be an issue with namespace packages.
58         # Non-namespace-package libs (e.g., charmhelpers) are available
59         # without having to reload the interpreter. :/
60         reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
61
62
63 def reload_interpreter(python):
64     """
65     Reload the python interpreter to ensure that all deps are available.
66
67     Newly installed modules in namespace packages sometimes seemt to
68     not be picked up by Python 3.
69     """
70     os.execle(python, python, sys.argv[0], os.environ)
71
72
73 def apt_install(packages):
74     """
75     Install apt packages.
76
77     This ensures a consistent set of options that are often missed but
78     should really be set.
79     """
80     if isinstance(packages, (str, bytes)):
81         packages = [packages]
82
83     env = os.environ.copy()
84
85     if 'DEBIAN_FRONTEND' not in env:
86         env['DEBIAN_FRONTEND'] = 'noninteractive'
87
88     cmd = ['apt-get',
89            '--option=Dpkg::Options::=--force-confold',
90            '--assume-yes',
91            'install']
92     check_call(cmd + packages, env=env)