Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only upgrade jupyterhub in user env when upgrading tljh, ensure pip>=23.1.2 in user env #890

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/unit-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,7 @@ jobs:
${{ hashFiles('setup.py', 'dev-requirements.txt', '.github/workflows/unit-test.yaml') }}

- name: Install Python dependencies
# Keep pip version pinning in sync with the one in bootstrap.py!
# See changelog at https://pip.pypa.io/en/latest/news/#changelog
run: |
python3 -m pip install -U "pip==23.1.*"
python3 -m pip install -r dev-requirements.txt
python3 -m pip install -e .
pip freeze
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
include tljh/systemd-units/*
include tljh/*.tpl
include tljh/requirements-base.txt
include tljh/requirements-*.txt
5 changes: 1 addition & 4 deletions bootstrap/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,8 @@ def serve_forever(server):
os.makedirs(hub_env_prefix, exist_ok=True)
run_subprocess(["python3", "-m", "venv", hub_env_prefix])

# Upgrade pip
# Keep pip version pinning in sync with the one in unit-test.yml!
# See changelog at https://pip.pypa.io/en/latest/news/#changelog
logger.info("Upgrading pip...")
run_subprocess([hub_env_pip, "install", "--upgrade", "pip==23.1.*"])
run_subprocess([hub_env_pip, "install", "--upgrade", "pip"])

# Install/upgrade TLJH installer
tljh_install_cmd = [hub_env_pip, "install", "--upgrade"]
Expand Down
85 changes: 47 additions & 38 deletions tljh/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,25 +106,13 @@ def ensure_jupyterhub_package(prefix):
hub environment be installed with pip prevents accidental mixing of python
and conda packages!
"""
# Install pycurl. JupyterHub prefers pycurl over SimpleHTTPClient automatically
# pycurl is generally more bugfree - see https://github.com/jupyterhub/the-littlest-jupyterhub/issues/289
# build-essential is also generally useful to everyone involved, and required for pycurl
# Install dependencies for installing pycurl via pip, where build-essential
# is generally useful for installing other packages as well.
apt.install_packages(["libssl-dev", "libcurl4-openssl-dev", "build-essential"])
conda.ensure_pip_packages(prefix, ["pycurl==7.*"], upgrade=True)

conda.ensure_pip_packages(
conda.ensure_pip_requirements(
prefix,
[
"jupyterhub==4.*",
"jupyterhub-systemdspawner==0.17.*",
"jupyterhub-firstuseauthenticator==1.*",
"jupyterhub-nativeauthenticator==1.*",
"jupyterhub-ldapauthenticator==1.*",
"jupyterhub-tmpauthenticator==0.6.*",
"oauthenticator==15.*",
"jupyterhub-idle-culler==1.*",
"git+https://github.com/yuvipanda/jupyterhub-configurator@317759e17c8e48de1b1352b836dac2a230536dba",
],
os.path.join(HERE, "requirements-hub-env.txt"),
upgrade=True,
)
traefik.ensure_traefik_binary(prefix)
Expand Down Expand Up @@ -158,9 +146,10 @@ def ensure_usergroups():

# minimum versions of packages
MINIMUM_VERSIONS = {
# if conda/mamba are lower than this, upgrade them before installing the user packages
# if conda/mamba/pip are lower than this, upgrade them before installing the user packages
"mamba": "0.16.0",
"conda": "4.10",
"pip": "23.1.2",
# minimum Python version (if not matched, abort to avoid big disruptive updates)
"python": "3.9",
}
Expand Down Expand Up @@ -197,30 +186,29 @@ def ensure_user_environment(user_requirements_txt_file):

# Check the existing environment for what to do
package_versions = conda.get_conda_package_versions(USER_ENV_PREFIX)
is_fresh_install = not package_versions

# Case 1: no existing environment
if not package_versions:
# 1a. no environment, but prefix exists.
# Abort to avoid clobbering something we don't recognize
if is_fresh_install:
# If no Python environment is detected but a folder exists we abort to
# avoid clobbering something we don't recognize.
if os.path.exists(USER_ENV_PREFIX) and os.listdir(USER_ENV_PREFIX):
msg = f"Found non-empty directory that is not a conda install in {USER_ENV_PREFIX}. Please remove it (or rename it to preserve files) and run tljh again."
logger.error(msg)
raise OSError(msg)

# 1b. No environment, directory empty or doesn't exist
# start fresh install
logger.info("Downloading & setting up user environment...")
installer_url, installer_sha256 = _mambaforge_url()
with conda.download_miniconda_installer(
installer_url, installer_sha256
) as installer_path:
conda.install_miniconda(installer_path, USER_ENV_PREFIX)
package_versions = conda.get_conda_package_versions(USER_ENV_PREFIX)

# quick sanity check: we should have conda and mamba!
assert "conda" in package_versions
assert "mamba" in package_versions

# next, check Python
# Check Python version
python_version = package_versions["python"]
logger.debug(f"Found python={python_version} in {USER_ENV_PREFIX}")
if V(python_version) < V(MINIMUM_VERSIONS["python"]):
Expand All @@ -232,10 +220,14 @@ def ensure_user_environment(user_requirements_txt_file):
logger.error(msg)
raise ValueError(msg)

# at this point, we know we have an env ready with conda and are going to start installing
# first, check if we should upgrade/install conda and/or mamba
# Ensure minimum versions of the following packages by upgrading to the
# latest if below that version.
#
# - conda/mamba, via conda-forge
# - pip, via PyPI
#
to_upgrade = []
for pkg in ("conda", "mamba"):
for pkg in ("conda", "mamba", "pip"):
version = package_versions.get(pkg)
min_version = MINIMUM_VERSIONS[pkg]
if not version:
Expand All @@ -249,20 +241,37 @@ def ensure_user_environment(user_requirements_txt_file):
)
to_upgrade.append(pkg)

if to_upgrade:
conda.ensure_conda_packages(
cf_pkgs_to_upgrade = list(set(to_upgrade) & {"conda", "mamba"})
if cf_pkgs_to_upgrade:
conda.ensure_conda_packages(
USER_ENV_PREFIX,
# we _could_ explicitly pin Python here,
# but conda already does this by default
cf_pkgs_to_upgrade,
)
pypi_pkgs_to_upgrade = list(set(to_upgrade) & {"pip"})
if pypi_pkgs_to_upgrade:
conda.ensure_pip_packages(
USER_ENV_PREFIX,
pypi_pkgs_to_upgrade,
upgrade=True,
)

# Install/upgrade the jupyterhub version in the user env based on the
# version specification used for the hub env.
#
with open(os.path.join(HERE, "requirements-hub-env.txt")) as f:
jh_version_spec = [l for l in f if l.startswith("jupyterhub>=")][0]
conda.ensure_pip_packages(USER_ENV_PREFIX, [jh_version_spec], upgrade=True)

# Install user environment extras for initial installations
#
if is_fresh_install:
conda.ensure_pip_requirements(
USER_ENV_PREFIX,
# we _could_ explicitly pin Python here,
# but conda already does this by default
to_upgrade,
os.path.join(HERE, "requirements-user-env-extras.txt"),
)

conda.ensure_pip_requirements(
USER_ENV_PREFIX,
os.path.join(HERE, "requirements-base.txt"),
upgrade=True,
)

if user_requirements_txt_file:
# FIXME: This currently fails hard, should fail soft and not abort installer
conda.ensure_pip_requirements(
Expand Down
29 changes: 29 additions & 0 deletions tljh/requirements-hub-env.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# When tljh.installer runs, the hub' environment as typically found in
# /opt/tljh/hub, is upgraded to use these packages.
#
# When a new release is made, the lower bounds should be incremented to the
# latest release to help us narrow the versions based on knowing what tljh
# version is installed from inspecting this file.
#
# If a dependency is bumped to a new major version, we should make a major
# version release of tljh.
#
jupyterhub>=4.0.0,<5
jupyterhub-systemdspawner>=0.17.0,<5
jupyterhub-firstuseauthenticator>=1.0.0,<2
jupyterhub-nativeauthenticator>=1.1.0,<2
jupyterhub-ldapauthenticator>=1.3.2,<2
jupyterhub-tmpauthenticator>=0.6.0,<0.7
oauthenticator>=15.1.0,<16
jupyterhub-idle-culler>=1.2.1,<2
git+https://github.com/yuvipanda/jupyterhub-configurator@317759e17c8e48de1b1352b836dac2a230536dba

# pycurl is installed to improve reliability and performance for when JupyterHub
# makes web requests. JupyterHub will use tornado's CurlAsyncHTTPClient when
# making requests over tornado's SimpleHTTPClient automatically if pycurl is
# installed.
#
# ref: https://www.tornadoweb.org/en/stable/httpclient.html#module-tornado.simple_httpclient
# ref: https://github.com/jupyterhub/the-littlest-jupyterhub/issues/289
#
pycurl>=7.45.2,<8
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# When tljh.installer runs, the users' environment as typically found in
# /opt/tljh/user, is setup with these packages.
# /opt/tljh/user, is installed with these packages.
#
# Whats listed here represents additional packages that the distributions
# installs initially, but doesn't upgrade as tljh is upgraded.
#
# WARNING: The order of these dependencies matters, this was observed when using
# the requirements-txt-fixer pre-commit hook that sorted them and made
# our integration tests fail.
#
# JupyterHub + notebook package are base requirements for user environment
jupyterhub==4.*
notebook==6.*
# Install additional notebook frontends!
jupyterlab==3.*
# nbgitpuller for easily pulling in Git repositories
nbgitpuller==1.*
Expand Down