mirror of
https://github.com/jupyterhub/the-littlest-jupyterhub.git
synced 2025-12-18 21:54:05 +08:00
Rewrite bootstrapper in Python
- This was going to get too complex for bash. Only way to kill those scripts is before they get too complex. - Better progress messages from bootstrapper. - Differentiate between bootstrapper & installer - Cleanup documentation a little bit
This commit is contained in:
@@ -74,7 +74,7 @@ jobs:
|
||||
name: run tljh installer
|
||||
command: |
|
||||
docker cp . tljh-systemd-ci:/srv/src
|
||||
docker exec -it tljh-systemd-ci bash /srv/src/installer/install.bash
|
||||
docker exec -it tljh-systemd-ci python3 /srv/src/bootstrap/bootstrap.py
|
||||
|
||||
- run:
|
||||
name: check jupyterhub is up
|
||||
|
||||
@@ -21,8 +21,8 @@ RUN systemctl set-default multi-user.target
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
|
||||
# Set up image to be useful out of the box for development & CI
|
||||
ENV TLJH_INSTALL_PIP_FLAGS="--no-cache-dir -e"
|
||||
ENV TLJH_INSTALL_PIP_SPEC=/srv/src
|
||||
ENV TLJH_BOOTSTRAP_DEV=yes
|
||||
ENV TLJH_BOOTSTRAP_PIP_SPEC=/srv/src
|
||||
ENV PATH=/opt/tljh/hub/bin:${PATH}
|
||||
|
||||
CMD ["/bin/bash", "-c", "exec /sbin/init --log-target=journal 3>&1"]
|
||||
|
||||
13
README.rst
13
README.rst
@@ -18,14 +18,5 @@ more information.
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
On a fresh Ubuntu 18.04 server, you can install The Littlest JupyterHub with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/installer/install.bash | sudo bash -
|
||||
|
||||
This takes 2-5 minutes to run. When completed, you can access your new JupyterHub
|
||||
at the public IP of your server (on the default http port 80)!
|
||||
|
||||
For more information (including other installation methods), check out the
|
||||
`documentation <https://the-littlest-jupyterhub.readthedocs.io>`_.
|
||||
Install The Littlest JupyterHub (TLJH) in under 10 minutes by following the
|
||||
`quickstart tutorial <http://the-littlest-jupyterhub.readthedocs.io/en/latest/tutorials/quickstart.html>`_.
|
||||
|
||||
125
bootstrap/bootstrap.py
Normal file
125
bootstrap/bootstrap.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
Bootstrap an installation of TLJH.
|
||||
|
||||
Sets up just enough TLJH environments to invoke tljh.installer.
|
||||
|
||||
This script is run as:
|
||||
|
||||
curl <script-url> | sudo python3 -
|
||||
|
||||
Constraints:
|
||||
- Be compatible with Python 3.4 (since we support Ubuntu 16.04)
|
||||
- Use stdlib modules only
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import urllib.request
|
||||
import contextlib
|
||||
import hashlib
|
||||
import tempfile
|
||||
|
||||
|
||||
def md5_file(fname):
|
||||
"""
|
||||
Return md5 of a given filename
|
||||
|
||||
Copied from https://stackoverflow.com/a/3431838
|
||||
"""
|
||||
hash_md5 = hashlib.md5()
|
||||
with open(fname, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hash_md5.update(chunk)
|
||||
return hash_md5.hexdigest()
|
||||
|
||||
|
||||
def check_miniconda_version(prefix, version):
|
||||
"""
|
||||
Return true if a miniconda install with version exists at prefix
|
||||
"""
|
||||
try:
|
||||
return subprocess.check_output([
|
||||
os.path.join(prefix, 'bin', 'conda'),
|
||||
'-V'
|
||||
]).decode().strip() == 'conda {}'.format(version)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
# Conda doesn't exist, or wrong version
|
||||
return False
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def download_miniconda_installer(version):
|
||||
md5sums = {
|
||||
'4.5.4': "a946ea1d0c4a642ddf0c3a26a18bb16d"
|
||||
}
|
||||
|
||||
if version not in md5sums:
|
||||
raise ValueError(
|
||||
'minicondaversion {} not supported. Supported version:'.format(
|
||||
version, ' '.join(md5sums.keys())
|
||||
))
|
||||
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
installer_url = "https://repo.continuum.io/miniconda/Miniconda3-{}-Linux-x86_64.sh".format(version)
|
||||
urllib.request.urlretrieve(installer_url, f.name)
|
||||
|
||||
if md5_file(f.name) != md5sums[version]:
|
||||
raise Exception('md5 hash mismatch! Downloaded file corrupted')
|
||||
|
||||
yield f.name
|
||||
|
||||
|
||||
def install_miniconda(installer_path, prefix):
|
||||
subprocess.check_output([
|
||||
'/bin/bash',
|
||||
installer_path,
|
||||
'-u', '-b',
|
||||
'-p', prefix
|
||||
], stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def pip_install(prefix, packages, editable=False):
|
||||
flags = '--no-cache-dir --upgrade'
|
||||
if editable:
|
||||
flags += '--editable'
|
||||
subprocess.check_output([
|
||||
os.path.join(prefix, 'bin', 'python3'),
|
||||
'-m', 'pip',
|
||||
'install', '--no-cache-dir',
|
||||
] + packages)
|
||||
|
||||
|
||||
def main():
|
||||
install_prefix = os.environ.get('TLJH_INSTALL_PREFIX', '/opt/tljh')
|
||||
hub_prefix = os.path.join(install_prefix, 'hub')
|
||||
miniconda_version = '4.5.4'
|
||||
|
||||
print('Checking if TLJH is already installed...')
|
||||
if not check_miniconda_version(hub_prefix, miniconda_version):
|
||||
initial_setup = True
|
||||
print('Downloading & setting up hub environment...')
|
||||
with download_miniconda_installer(miniconda_version) as installer_path:
|
||||
install_miniconda(installer_path, hub_prefix)
|
||||
print('Hub environment set up!')
|
||||
else:
|
||||
initial_setup = False
|
||||
print('TLJH is already installed, will try to upgrade')
|
||||
|
||||
if initial_setup:
|
||||
print('Setting up TLJH installer...')
|
||||
else:
|
||||
print('Upgrading TLJH installer...')
|
||||
|
||||
pip_install(hub_prefix, [
|
||||
os.environ.get('TLJH_BOOTSTRAP_PIP_SPEC', 'git+https://github.com/yuvipanda/the-littlest-jupyterhub.git')
|
||||
], editable=os.environ.get('TLJH_BOOTSTRAP_DEV', 'no') == 'yes')
|
||||
|
||||
print('Starting TLJH installer...')
|
||||
os.execl(
|
||||
os.path.join(hub_prefix, 'bin', 'python3'),
|
||||
os.path.join(hub_prefix, 'bin', 'python3'),
|
||||
'-m',
|
||||
'tljh.installer'
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -34,13 +34,13 @@ The easiest & safest way to develop & test TLJH is with `Docker <https://www.doc
|
||||
|
||||
sudo docker exec -it tljh-dev /bin/bash
|
||||
|
||||
#. Run the installer from inside the container (see step above):
|
||||
#. Run the bootstrapper from inside the container (see step above):
|
||||
The container image is already set up to default to a ``dev`` install, so
|
||||
it'll install from your local repo rather than from github.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
bash /srv/src/installer/install.bash
|
||||
python3 /srv/src/bootstrap/bootstrap.py
|
||||
|
||||
The primary hub environment will also be in your PATH already for convenience.
|
||||
|
||||
@@ -51,8 +51,8 @@ The easiest & safest way to develop & test TLJH is with `Docker <https://www.doc
|
||||
#. Make some changes to the repository. You can test easily depending on what
|
||||
you changed.
|
||||
|
||||
* If you changed the ``installer/install.bash`` script or any of its dependencies,
|
||||
you can test it by running ``bash /srv/src/installer/install.bash``.
|
||||
* If you changed the ``bootstrap/bootstrap.py`` script or any of its dependencies,
|
||||
you can test it by running ``python3 /srv/src/bootstrap/bootstrap.py``.
|
||||
|
||||
* If you changed the ``tljh/installer.py`` code (or any of its dependencies),
|
||||
you can test it by running ``python3 -m tljh.installer``.
|
||||
|
||||
@@ -10,7 +10,7 @@ The quick way to install The Littlest JupyterHub (tljh) is:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/installer/install.bash | sudo bash -
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/bootstrap/bootstrap.py | sudo python3 -
|
||||
|
||||
This takes 2-5 minutes to run. When completed, you can access your new JupyterHub
|
||||
at the public IP of your server!
|
||||
@@ -21,7 +21,7 @@ after installation.
|
||||
Slightly less quick installation
|
||||
--------------------------------
|
||||
|
||||
If you can read ``bash`` and are nervous about the previous installation method,
|
||||
If you can read ``python3`` and are nervous about the previous installation method,
|
||||
you can inspect the installer script before running it.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ you can inspect the installer script before running it.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/installer/install.bash -o install.bash
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/bootstrap/bootstrap.py -o bootstrap.py
|
||||
|
||||
2. Read the install script source using your favorite text editor
|
||||
|
||||
@@ -37,6 +37,6 @@ you can inspect the installer script before running it.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sudo install.bash
|
||||
sudo python3 bootstrap.py
|
||||
|
||||
This should have the exact same effects as the quick installer method.
|
||||
|
||||
@@ -11,10 +11,10 @@ On a fresh Ubuntu 18.04 server, you can install The Littlest JupyterHub with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/installer/install.bash | sudo bash -
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/bootstrap/bootstrap.py | sudo python3 -
|
||||
|
||||
This takes 2-5 minutes to run. When completed, you can access your new JupyterHub
|
||||
at the public IP of your server!
|
||||
at the public IP of your server! Read the :ref:`tutorial_quickstart` next.
|
||||
|
||||
If this installation method (``curl <arbitrary-url> | sudo bash -``)
|
||||
makes you nervous, check out the :ref:`other installation methods <installation>` we support!
|
||||
|
||||
@@ -25,7 +25,7 @@ Step 1: Install the Littlest JupyterHub (TLJH)
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/installer/install.bash | sudo bash -
|
||||
curl https://raw.githubusercontent.com/yuvipanda/the-littlest-jupyterhub/master/bootstrap/bootstrap.py | sudo python3 -
|
||||
|
||||
This takes about 1-3 minutes to finish. When completed, you can visit the
|
||||
public IP of your server to use your JupyterHub! You can log in with any username
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/bash
|
||||
set -exuo pipefail
|
||||
|
||||
# Set up defaults for configurable env vars
|
||||
TLJH_INSTALL_PREFIX=${TLJH_INSTALL_PREFIX:-/opt/tljh}
|
||||
TLJH_INSTALL_PIP_SPEC=${TLJH_INSTALL_PIP_SPEC:-git+https://github.com/yuvipanda/the-littlest-jupyterhub.git}
|
||||
TLJH_INSTALL_PIP_FLAGS=${TLJH_INSTALL_PIP_FLAGS:---no-cache-dir}
|
||||
|
||||
|
||||
function install_miniconda {
|
||||
CONDA_DIR=${1}
|
||||
CONDA_VERSION=4.5.4
|
||||
if [ -e ${CONDA_DIR}/bin/conda ]; then
|
||||
if [ "$(${CONDA_DIR}/bin/conda -V)" == "conda ${CONDA_VERSION}" ]; then
|
||||
# The given ${CONDA_DIR} already has a conda with given version
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
URL="https://repo.continuum.io/miniconda/Miniconda3-${CONDA_VERSION}-Linux-x86_64.sh"
|
||||
INSTALLER_PATH=/tmp/miniconda-installer.sh
|
||||
|
||||
curl -o ${INSTALLER_PATH} ${URL}
|
||||
chmod +x ${INSTALLER_PATH}
|
||||
|
||||
# Only MD5 checksums are available for miniconda
|
||||
# Can be obtained from https://repo.continuum.io/miniconda/
|
||||
MD5SUM="a946ea1d0c4a642ddf0c3a26a18bb16d"
|
||||
|
||||
if ! echo "${MD5SUM} ${INSTALLER_PATH}" | md5sum --quiet -c -; then
|
||||
echo "md5sum mismatch for ${INSTALLER_PATH}, exiting!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash ${INSTALLER_PATH} -u -b -p ${CONDA_DIR}
|
||||
|
||||
# Allow easy direct installs from conda forge
|
||||
${CONDA_DIR}/bin/conda config --system --add channels conda-forge
|
||||
|
||||
# Do not attempt to auto update conda or dependencies
|
||||
${CONDA_DIR}/bin/conda config --system --set auto_update_conda false
|
||||
${CONDA_DIR}/bin/conda config --system --set show_channel_urls true
|
||||
}
|
||||
|
||||
HUB_CONDA_DIR=${TLJH_INSTALL_PREFIX}/hub
|
||||
install_miniconda ${HUB_CONDA_DIR}
|
||||
${HUB_CONDA_DIR}/bin/pip install --upgrade ${TLJH_INSTALL_PIP_FLAGS} ${TLJH_INSTALL_PIP_SPEC}
|
||||
|
||||
${HUB_CONDA_DIR}/bin/python3 -m tljh.installer
|
||||
@@ -14,8 +14,7 @@ HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
def ensure_jupyterhub_service(prefix):
|
||||
"""
|
||||
Ensure JupyterHub & CHP Services are set up properly
|
||||
"""
|
||||
Ensure JupyterHub & CHP Services are set up properly """
|
||||
with open(os.path.join(HERE, 'systemd-units', 'jupyterhub.service')) as f:
|
||||
hub_unit_template = f.read()
|
||||
|
||||
@@ -66,31 +65,42 @@ def ensure_jupyterhub_package(prefix):
|
||||
])
|
||||
|
||||
|
||||
ensure_jupyterhub_package(HUB_ENV_PREFIX)
|
||||
ensure_jupyterhub_service(HUB_ENV_PREFIX)
|
||||
def main():
|
||||
print("Setting up JupyterHub...")
|
||||
ensure_jupyterhub_package(HUB_ENV_PREFIX)
|
||||
ensure_jupyterhub_service(HUB_ENV_PREFIX)
|
||||
|
||||
user.ensure_group('jupyterhub-admins')
|
||||
user.ensure_group('jupyterhub-users')
|
||||
print("Setting up system user groups...")
|
||||
user.ensure_group('jupyterhub-admins')
|
||||
user.ensure_group('jupyterhub-users')
|
||||
|
||||
with open('/etc/sudoers.d/jupyterhub-admins', 'w') as f:
|
||||
# JupyterHub admins should have full passwordless sudo access
|
||||
f.write('%jupyterhub-admins ALL = (ALL) NOPASSWD: ALL\n')
|
||||
# `sudo -E` should preserve the $PATH we set. This allows
|
||||
# admins in jupyter terminals to do `sudo -E pip install <package>`,
|
||||
# `pip` is in the $PATH we set in jupyterhub_config.py to include the user conda env.
|
||||
f.write('Defaults exempt_group = jupyterhub-admins\n')
|
||||
print("Grainting passwordless sudo to JupyterHub admins...")
|
||||
with open('/etc/sudoers.d/jupyterhub-admins', 'w') as f:
|
||||
# JupyterHub admins should have full passwordless sudo access
|
||||
f.write('%jupyterhub-admins ALL = (ALL) NOPASSWD: ALL\n')
|
||||
# `sudo -E` should preserve the $PATH we set. This allows
|
||||
# admins in jupyter terminals to do `sudo -E pip install <package>`,
|
||||
# `pip` is in the $PATH we set in jupyterhub_config.py to include the user conda env.
|
||||
f.write('Defaults exempt_group = jupyterhub-admins\n')
|
||||
|
||||
conda.ensure_conda_env(USER_ENV_PREFIX)
|
||||
conda.ensure_conda_packages(USER_ENV_PREFIX, [
|
||||
# Conda's latest version is on conda much more so than on PyPI.
|
||||
'conda==4.5.4'
|
||||
])
|
||||
print("Setting up user environment...")
|
||||
conda.ensure_conda_env(USER_ENV_PREFIX)
|
||||
conda.ensure_conda_packages(USER_ENV_PREFIX, [
|
||||
# Conda's latest version is on conda much more so than on PyPI.
|
||||
'conda==4.5.4'
|
||||
])
|
||||
|
||||
conda.ensure_pip_packages(USER_ENV_PREFIX, [
|
||||
# JupyterHub + notebook package are base requirements for user environment
|
||||
'jupyterhub==0.9.0',
|
||||
'notebook==5.5.0',
|
||||
# Install additional notebook frontends!
|
||||
'jupyterlab==0.32.1',
|
||||
'nteract-on-jupyter==1.8.1'
|
||||
])
|
||||
conda.ensure_pip_packages(USER_ENV_PREFIX, [
|
||||
# JupyterHub + notebook package are base requirements for user environment
|
||||
'jupyterhub==0.9.0',
|
||||
'notebook==5.5.0',
|
||||
# Install additional notebook frontends!
|
||||
'jupyterlab==0.32.1',
|
||||
'nteract-on-jupyter==1.8.1'
|
||||
])
|
||||
|
||||
print("Done!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user