def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $CHARM_DIR/exec.d. execd_preinstall() # ensure that $CHARM_DIR/bin is on the path, for helper scripts os.environ["PATH"] += ":%s" % os.path.join(os.environ["CHARM_DIR"], "bin") venv = os.path.abspath("../.venv") vbin = os.path.join(venv, "bin") vpip = os.path.join(vbin, "pip") vpy = os.path.join(vbin, "python") if os.path.exists("wheelhouse/.bootstrapped"): from charms import layer cfg = layer.options("basic") if cfg.get("use_venv") and ".venv" not in sys.executable: # activate the venv os.environ["PATH"] = ":".join([vbin, os.environ["PATH"]]) reload_interpreter(vpy) return # bootstrap wheelhouse if os.path.exists("wheelhouse"): with open("/root/.pydistutils.cfg", "w") as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) charm_dir = os.environ["CHARM_DIR"] fp.writelines( ["[easy_install]\n", "allow_hosts = ''\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir)] ) apt_install(["python3-pip", "python3-setuptools", "python3-yaml"]) from charms import layer cfg = layer.options("basic") # include packages defined in layer.yaml apt_install(cfg.get("packages", [])) # if we're using a venv, set it up if cfg.get("use_venv"): if not os.path.exists(venv): apt_install(["python-virtualenv"]) cmd = ["virtualenv", "-ppython3", "--never-download", venv] if cfg.get("include_system_packages"): cmd.append("--system-site-packages") check_call(cmd) os.environ["PATH"] = ":".join([vbin, os.environ["PATH"]]) pip = vpip else: pip = "pip3" # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists("/usr/bin/pip"): shutil.copy2("/usr/bin/pip", "/usr/bin/pip.save") # need newer pip, to fix spurious Double Requirement error: # https://github.com/pypa/pip/issues/56 check_call([pip, "install", "-U", "--no-index", "-f", "wheelhouse", "pip"]) # install the rest of the wheelhouse deps check_call([pip, "install", "-U", "--no-index", "-f", "wheelhouse"] + glob("wheelhouse/*")) if not cfg.get("use_venv"): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists("/usr/bin/pip.save"): shutil.copy2("/usr/bin/pip.save", "/usr/bin/pip") os.remove("/usr/bin/pip.save") os.remove("/root/.pydistutils.cfg") # flag us as having already bootstrapped so we don't do it again open("wheelhouse/.bootstrapped", "w").close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ reload_interpreter(vpy if cfg.get("use_venv") else sys.argv[0])
def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $CHARM_DIR/exec.d. execd_preinstall() venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') vpy = os.path.join(vbin, 'python') if os.path.exists('wheelhouse/.bootstrapped'): from charms import layer cfg = layer.options('basic') if cfg.get('use_venv') and '.venv' not in sys.executable: # activate the venv os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) reload_interpreter(vpy) return # bootstrap wheelhouse if os.path.exists('wheelhouse'): with open('/root/.pydistutils.cfg', 'w') as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) charm_dir = os.environ['CHARM_DIR'] fp.writelines([ "[easy_install]\n", "allow_hosts = ''\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir), ]) apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml']) from charms import layer cfg = layer.options('basic') # include packages defined in layer.yaml apt_install(cfg.get('packages', [])) # if we're using a venv, set it up if cfg.get('use_venv'): if not os.path.exists(venv): apt_install(['python-virtualenv']) cmd = ['virtualenv', '-ppython3', '--never-download', venv] if cfg.get('include_system_packages'): cmd.append('--system-site-packages') check_call(cmd) os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) pip = vpip else: pip = 'pip3' # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip'): shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') # need newer pip, to fix spurious Double Requirement error: # https://github.com/pypa/pip/issues/56 check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) # install the rest of the wheelhouse deps check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*')) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') os.remove('/root/.pydistutils.cfg') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $JUJU_CHARM_DIR/exec.d. execd_preinstall() # ensure that $JUJU_CHARM_DIR/bin is on the path, for helper scripts series = get_series() # OMG?! is build-essentials needed? ubuntu_packages = [ 'python3-pip', 'python3-setuptools', 'python3-yaml', 'python3-dev', 'python3-wheel', 'build-essential' ] # I'm not going to "yum group info "Development Tools" # omitting above madness centos_packages = [ 'python3-pip', 'python3-setuptools', 'python3-devel', 'python3-wheel' ] packages_needed = [] if 'centos' in series: packages_needed = centos_packages else: packages_needed = ubuntu_packages charm_dir = os.environ['JUJU_CHARM_DIR'] os.environ['PATH'] += ':%s' % os.path.join(charm_dir, 'bin') venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') vpy = os.path.join(vbin, 'python') hook_name = os.path.basename(sys.argv[0]) is_bootstrapped = os.path.exists('wheelhouse/.bootstrapped') is_charm_upgrade = hook_name == 'upgrade-charm' is_series_upgrade = hook_name == 'post-series-upgrade' post_upgrade = os.path.exists('wheelhouse/.upgrade') is_upgrade = not post_upgrade and (is_charm_upgrade or is_series_upgrade) if is_bootstrapped and not is_upgrade: # older subordinates might have downgraded charm-env, so we should # restore it if necessary install_or_update_charm_env() activate_venv() # the .upgrade file prevents us from getting stuck in a loop # when re-execing to activate the venv; at this point, we've # activated the venv, so it's safe to clear it if post_upgrade: os.unlink('wheelhouse/.upgrade') return if is_series_upgrade and os.path.exists(venv): # series upgrade should do a full clear of the venv, rather than just # updating it, to bring in updates to Python itself shutil.rmtree(venv) if is_upgrade: if os.path.exists('wheelhouse/.bootstrapped'): os.unlink('wheelhouse/.bootstrapped') open('wheelhouse/.upgrade', 'w').close() # bootstrap wheelhouse if os.path.exists('wheelhouse'): with open('/root/.pydistutils.cfg', 'w') as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) fp.writelines([ "[easy_install]\n", "allow_hosts = ''\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir), ]) if 'centos' in series: yum_install(packages_needed) else: apt_install(packages_needed) from charms.layer import options cfg = options.get('basic') # include packages defined in layer.yaml if 'centos' in series: yum_install(cfg.get('packages', [])) else: apt_install(cfg.get('packages', [])) # if we're using a venv, set it up if cfg.get('use_venv'): if not os.path.exists(venv): series = get_series() if series in ('ubuntu12.04', 'precise', 'ubuntu14.04', 'trusty'): apt_install(['python-virtualenv']) elif 'centos' in series: yum_install(['python-virtualenv']) else: apt_install(['virtualenv']) cmd = ['virtualenv', '-ppython3', '--never-download', venv] if cfg.get('include_system_packages'): cmd.append('--system-site-packages') check_call(cmd) os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) pip = vpip else: pip = 'pip3' # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip'): shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') # need newer pip, to fix spurious Double Requirement error: # https://github.com/pypa/pip/issues/56 check_call( [pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) # per https://github.com/juju-solutions/layer-basic/issues/110 # this replaces the setuptools that was copied over from the system on # venv create with latest setuptools and adds setuptools_scm check_call([ pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'setuptools', 'setuptools-scm' ]) # install the rest of the wheelhouse deps check_call([ pip, 'install', '-U', '--ignore-installed', '--no-index', '-f', 'wheelhouse' ] + glob('wheelhouse/*')) # re-enable installation from pypi os.remove('/root/.pydistutils.cfg') # install pyyaml for centos7, since, unlike the ubuntu image, the # default image for centos doesn't include pyyaml; see the discussion: # https://discourse.jujucharms.com/t/charms-for-centos-lets-begin if 'centos' in series: check_call([pip, 'install', '-U', 'pyyaml']) # install python packages from layer options if cfg.get('python_packages'): check_call([pip, 'install', '-U'] + cfg.get('python_packages')) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') # setup wrappers to ensure envs are used for scripts install_or_update_charm_env() for wrapper in ('charms.reactive', 'charms.reactive.sh', 'chlp', 'layer_option'): src = os.path.join('/usr/local/sbin', 'charm-env') dst = os.path.join('/usr/local/sbin', wrapper) if not os.path.exists(dst): os.symlink(src, dst) if cfg.get('use_venv'): shutil.copy2('bin/layer_option', vbin) else: shutil.copy2('bin/layer_option', '/usr/local/bin/') # re-link the charm copy to the wrapper in case charms # call bin/layer_option directly (as was the old pattern) os.remove('bin/layer_option') os.symlink('/usr/local/sbin/layer_option', 'bin/layer_option') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
def exec_install_hooks(): log("Invoking pre-install hooks under hooks/install.d") execd_preinstall("hooks/install.d")
def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $JUJU_CHARM_DIR/exec.d. execd_preinstall() # ensure that $JUJU_CHARM_DIR/bin is on the path, for helper scripts charm_dir = os.environ['JUJU_CHARM_DIR'] os.environ['PATH'] += ':%s' % os.path.join(charm_dir, 'bin') venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') vpy = os.path.join(vbin, 'python') if os.path.exists('wheelhouse/.bootstrapped'): activate_venv() return # bootstrap wheelhouse if os.path.exists('wheelhouse'): with open('/root/.pydistutils.cfg', 'w') as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) fp.writelines([ "[easy_install]\n", "allow_hosts = ''\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir), ]) apt_install([ 'python3-pip', 'python3-setuptools', 'python3-yaml', 'python3-dev', ]) from charms import layer cfg = layer.options('basic') # include packages defined in layer.yaml apt_install(cfg.get('packages', [])) # if we're using a venv, set it up if cfg.get('use_venv'): if not os.path.exists(venv): series = lsb_release()['DISTRIB_CODENAME'] if series in ('precise', 'trusty'): apt_install(['python-virtualenv']) else: apt_install(['virtualenv']) cmd = ['virtualenv', '-ppython3', '--never-download', venv] if cfg.get('include_system_packages'): cmd.append('--system-site-packages') check_call(cmd) os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) pip = vpip else: pip = 'pip3' # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip'): shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') # need newer pip, to fix spurious Double Requirement error: # https://github.com/pypa/pip/issues/56 check_call( [pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) # per https://github.com/juju-solutions/layer-basic/issues/110 # this replaces the setuptools that was copied over from the system on # venv create with latest setuptools and adds setuptools_scm check_call([ pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'setuptools', 'setuptools-scm' ]) # install the rest of the wheelhouse deps check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*')) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') os.remove('/root/.pydistutils.cfg') # setup wrappers to ensure envs are used for scripts shutil.copy2('bin/charm-env', '/usr/local/sbin/') for wrapper in ('charms.reactive', 'charms.reactive.sh', 'chlp', 'layer_option'): src = os.path.join('/usr/local/sbin', 'charm-env') dst = os.path.join('/usr/local/sbin', wrapper) if not os.path.exists(dst): os.symlink(src, dst) if cfg.get('use_venv'): shutil.copy2('bin/layer_option', vbin) else: shutil.copy2('bin/layer_option', '/usr/local/bin/') # re-link the charm copy to the wrapper in case charms # call bin/layer_option directly (as was the old pattern) os.remove('bin/layer_option') os.symlink('/usr/local/sbin/layer_option', 'bin/layer_option') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $JUJU_CHARM_DIR/exec.d. execd_preinstall() # ensure that $JUJU_CHARM_DIR/bin is on the path, for helper scripts charm_dir = os.environ['JUJU_CHARM_DIR'] os.environ['PATH'] += ':%s' % os.path.join(charm_dir, 'bin') venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') # system default pip vpy = os.path.join(vbin, 'python') # ".bootstrapped" is a flag file. If it exists, meaning some other charms # have already done these steps so there is no need to go through # these steps again. if os.path.exists('wheelhouse/.bootstrapped'): activate_venv() return # determine host env dist = lsb_release()['DISTRIB_ID'].lower() # bootstrap wheelhouse if os.path.exists('wheelhouse'): with open('/root/.pydistutils.cfg', 'w') as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) fp.writelines([ "[easy_install]\n", "allow_hosts = ''\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir), ]) # Pre-install packages based on host env. if 'ubuntu' in dist: # Python27, required by CentOS7, pylxca apt_install([ 'python', # default python pkg 'python-pip', 'python-setuptools', 'python-yaml', 'python-dev', ]) elif 'cent' in dist: apt_install([ 'epel-release', 'python-setuptools', 'python-pip', 'python-yaml', 'python-devel', ]) # include packages defined in layer.yaml from charms import layer cfg = layer.options('basic') apt_install(cfg.get('packages', [])) # If using python virtualenv on Ubuntu host if 'ubuntu' in dist and cfg.get('use_venv'): if not os.path.exists(venv): series = lsb_release()['DISTRIB_CODENAME'] if series in ('precise', 'trusty'): apt_install(['python-virtualenv']) else: apt_install(['virtualenv']) cmd = ['virtualenv', '-ppython3', '--never-download', venv] if cfg.get('include_system_packages'): cmd.append('--system-site-packages') check_call(cmd) os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) pip = vpip # If installing python virtualenv on CentOS host elif 'cent' in dist and cfg.get('use_venv'): # TODO: how to install virtualenv in CentOS? pass # If NOT using virtualenv elif not cfg.get('use_venv'): if 'ubuntu' in dist: pip = 'pip' # Ubuntu using pip # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip'): shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') elif 'cent' in dist: pip = 'pip' # CentOS using default pip # need newer pip, to fix spurious Double Requirement error: # https://github.com/pypa/pip/issues/56 check_call( [pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip']) # install the rest of the wheelhouse deps output = check_output( [pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*')) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') os.remove('/root/.pydistutils.cfg') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ sys.path.append('/usr/local/lib/python2.7/dist-packages') reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
def bootstrap_charm_deps(): """ Set up the base charm dependencies so that the reactive system can run. """ # execd must happen first, before any attempt to install packages or # access the network, because sites use this hook to do bespoke # configuration and install secrets so the rest of this bootstrap # and the charm itself can actually succeed. This call does nothing # unless the operator has created and populated $JUJU_CHARM_DIR/exec.d. execd_preinstall() # ensure that $JUJU_CHARM_DIR/bin is on the path, for helper scripts series = get_series() # OMG?! is build-essentials needed? ubuntu_packages = [ 'python3-pip', 'python3-setuptools', 'python3-yaml', 'python3-dev', 'python3-wheel', 'build-essential' ] # I'm not going to "yum group info "Development Tools" # omitting above madness centos_packages = [ 'python3-pip', 'python3-setuptools', 'python3-devel', 'python3-wheel' ] packages_needed = [] if 'centos' in series: packages_needed = centos_packages else: packages_needed = ubuntu_packages charm_dir = os.environ['JUJU_CHARM_DIR'] os.environ['PATH'] += ':%s' % os.path.join(charm_dir, 'bin') venv = os.path.abspath('../.venv') vbin = os.path.join(venv, 'bin') vpip = os.path.join(vbin, 'pip') vpy = os.path.join(vbin, 'python') hook_name = os.path.basename(sys.argv[0]) is_bootstrapped = os.path.exists('wheelhouse/.bootstrapped') is_charm_upgrade = hook_name == 'upgrade-charm' is_series_upgrade = hook_name == 'post-series-upgrade' is_post_upgrade = os.path.exists('wheelhouse/.upgraded') is_upgrade = (not is_post_upgrade and (is_charm_upgrade or is_series_upgrade)) if is_bootstrapped and not is_upgrade: # older subordinates might have downgraded charm-env, so we should # restore it if necessary install_or_update_charm_env() activate_venv() # the .upgrade file prevents us from getting stuck in a loop # when re-execing to activate the venv; at this point, we've # activated the venv, so it's safe to clear it if is_post_upgrade: os.unlink('wheelhouse/.upgraded') return if os.path.exists(venv): try: # focal installs or upgrades prior to PR 160 could leave the venv # in a broken state which would prevent subsequent charm upgrades _load_installed_versions(vpip) except CalledProcessError: is_broken_venv = True else: is_broken_venv = False if is_upgrade or is_broken_venv: # All upgrades should do a full clear of the venv, rather than # just updating it, to bring in updates to Python itself shutil.rmtree(venv) if is_upgrade: if os.path.exists('wheelhouse/.bootstrapped'): os.unlink('wheelhouse/.bootstrapped') # bootstrap wheelhouse if os.path.exists('wheelhouse'): pre_eoan = series in ('ubuntu12.04', 'precise', 'ubuntu14.04', 'trusty', 'ubuntu16.04', 'xenial', 'ubuntu18.04', 'bionic') pydistutils_lines = [ "[easy_install]\n", "find_links = file://{}/wheelhouse/\n".format(charm_dir), "no_index=True\n", "index_url=\n", # deliberately nothing here; disables it. ] if pre_eoan: pydistutils_lines.append("allow_hosts = ''\n") with open('/root/.pydistutils.cfg', 'w') as fp: # make sure that easy_install also only uses the wheelhouse # (see https://github.com/pypa/pip/issues/410) fp.writelines(pydistutils_lines) if 'centos' in series: yum_install(packages_needed) else: apt_install(packages_needed) from charms.layer import options cfg = options.get('basic') # include packages defined in layer.yaml if 'centos' in series: yum_install(cfg.get('packages', [])) else: apt_install(cfg.get('packages', [])) # if we're using a venv, set it up if cfg.get('use_venv'): if not os.path.exists(venv): series = get_series() if series in ('ubuntu12.04', 'precise', 'ubuntu14.04', 'trusty'): apt_install(['python-virtualenv']) elif 'centos' in series: yum_install(['python-virtualenv']) else: apt_install(['virtualenv']) cmd = ['virtualenv', '-ppython3', '--never-download', venv] if cfg.get('include_system_packages'): cmd.append('--system-site-packages') check_call(cmd, env=_get_subprocess_env()) os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) pip = vpip else: pip = 'pip3' # save a copy of system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip'): shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') pre_install_pkgs = ['pip', 'setuptools', 'setuptools-scm'] # we bundle these packages to work around bugs in older versions (such # as https://github.com/pypa/pip/issues/56), but if the system already # provided a newer version, downgrading it can cause other problems _update_if_newer(pip, pre_install_pkgs) # install the rest of the wheelhouse deps (extract the pkg names into # a set so that we can ignore the pre-install packages and let pip # choose the best version in case there are multiple from layer # conflicts) pkgs = _load_wheelhouse_versions().keys() - set(pre_install_pkgs) reinstall_flag = '--force-reinstall' if not cfg.get('use_venv', True) and pre_eoan: reinstall_flag = '--ignore-installed' check_call([ pip, 'install', '-U', reinstall_flag, '--no-index', '--no-cache-dir', '-f', 'wheelhouse' ] + list(pkgs), env=_get_subprocess_env()) # re-enable installation from pypi os.remove('/root/.pydistutils.cfg') # install pyyaml for centos7, since, unlike the ubuntu image, the # default image for centos doesn't include pyyaml; see the discussion: # https://discourse.jujucharms.com/t/charms-for-centos-lets-begin if 'centos' in series: check_call([pip, 'install', '-U', 'pyyaml'], env=_get_subprocess_env()) # install python packages from layer options if cfg.get('python_packages'): check_call([pip, 'install', '-U'] + cfg.get('python_packages'), env=_get_subprocess_env()) if not cfg.get('use_venv'): # restore system pip to prevent `pip3 install -U pip` # from changing it if os.path.exists('/usr/bin/pip.save'): shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') os.remove('/usr/bin/pip.save') # setup wrappers to ensure envs are used for scripts install_or_update_charm_env() for wrapper in ('charms.reactive', 'charms.reactive.sh', 'chlp', 'layer_option'): src = os.path.join('/usr/local/sbin', 'charm-env') dst = os.path.join('/usr/local/sbin', wrapper) if not os.path.exists(dst): os.symlink(src, dst) if cfg.get('use_venv'): shutil.copy2('bin/layer_option', vbin) else: shutil.copy2('bin/layer_option', '/usr/local/bin/') # re-link the charm copy to the wrapper in case charms # call bin/layer_option directly (as was the old pattern) os.remove('bin/layer_option') os.symlink('/usr/local/sbin/layer_option', 'bin/layer_option') # flag us as having already bootstrapped so we don't do it again open('wheelhouse/.bootstrapped', 'w').close() if is_upgrade: # flag us as having already upgraded so we don't do it again open('wheelhouse/.upgraded', 'w').close() # Ensure that the newly bootstrapped libs are available. # Note: this only seems to be an issue with namespace packages. # Non-namespace-package libs (e.g., charmhelpers) are available # without having to reload the interpreter. :/ reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])