From 9f2a04ac4918983ca6dd246d6e82329f5cc0c1f2 Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Sun, 19 May 2019 22:44:49 -0700 Subject: [PATCH 1/3] Add more validation to bootstrap.py - Check for Python Version - Check if systemd is present - Provide more useful error message when running inside an unprepared docker container Ref #16 --- bootstrap/bootstrap.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py index 3a42fb2..adbc9fc 100644 --- a/bootstrap/bootstrap.py +++ b/bootstrap/bootstrap.py @@ -16,6 +16,7 @@ import os import subprocess import sys import logging +import shutil def get_os_release_variable(key): @@ -31,8 +32,10 @@ def get_os_release_variable(key): "source /etc/os-release && echo ${{{key}}}".format(key=key) ]).decode().strip() -def main(): - +def validate_host(): + """ + Make sure TLJH is installable in current host + """ # Support only Ubuntu 18.04+ distro = get_os_release_variable('ID') version = float(get_os_release_variable('VERSION_ID')) @@ -43,6 +46,20 @@ def main(): print('The Littlest JupyterHub requires Ubuntu 18.04 or higher') sys.exit(1) + if sys.version_info < (3, 5): + print("bootstrap.py must be run with at least Python 3.5") + sys.exit(1) + + if not (shutil.which('systemd') and shutil.which('systemctl')): + print("Systemd is required to run TLJH") + # Only fail running inside docker if systemd isn't present + if os.path.exists('/.dockerenv'): + print("Running inside a plain docker container isn't supported") + print("For local development, see http://tljh.jupyter.org/en/latest/contributing/dev-setup.html") + sys.exit(1) + +def main(): + validate_host() install_prefix = os.environ.get('TLJH_INSTALL_PREFIX', '/opt/tljh') hub_prefix = os.path.join(install_prefix, 'hub') From 0370b7513e301884bae2ab924897a6245c6acfed Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Sun, 19 May 2019 23:00:43 -0700 Subject: [PATCH 2/3] Add test for failure when running inside a plain docker container --- integration-tests/test_bootstrap.py | 33 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/integration-tests/test_bootstrap.py b/integration-tests/test_bootstrap.py index 5bf143f..a93fc5a 100644 --- a/integration-tests/test_bootstrap.py +++ b/integration-tests/test_bootstrap.py @@ -2,14 +2,9 @@ Test running bootstrap script in different circumstances """ import subprocess +from textwrap import dedent - -def test_ubuntu_too_old(): - """ - Error with a useful message when running in older Ubuntu - """ - container_name = 'old-distro-test' - +def run_bootstrap(container_name, image): # stop container if it is already running subprocess.run([ 'docker', 'rm', '-f', container_name @@ -17,7 +12,7 @@ def test_ubuntu_too_old(): # Start a detached Ubuntu 16.04 container subprocess.check_call([ - 'docker', 'run', '--detach', '--name', container_name, 'ubuntu:16.04', + 'docker', 'run', '--detach', '--name', container_name, image, '/bin/bash', '-c', 'sleep 1000s' ]) # Install python3 inside the ubuntu container @@ -35,10 +30,26 @@ def test_ubuntu_too_old(): 'bootstrap/', f'{container_name}:/srv' ]) - # Run bootstrap script, validate that it fails appropriately - output = subprocess.run([ + # Run bootstrap script, return the output + return subprocess.run([ 'docker', 'exec', '-i', container_name, 'python3', '/srv/bootstrap/bootstrap.py' ], check=False, stdout=subprocess.PIPE, encoding='utf-8') + +def test_ubuntu_too_old(): + """ + Error with a useful message when running in older Ubuntu + """ + output = run_bootstrap('old-distro-test', 'ubuntu:16.04') assert output.stdout == 'The Littlest JupyterHub requires Ubuntu 18.04 or higher\n' - assert output.returncode == 1 \ No newline at end of file + assert output.returncode == 1 + + +def test_inside_plain_docker(): + output = run_bootstrap('plain-docker-test', 'ubuntu:18.04') + assert output.stdout.strip() == dedent(""" + Systemd is required to run TLJH + Running inside a plain docker container isn't supported + For local development, see http://tljh.jupyter.org/en/latest/contributing/dev-setup.html + """).strip() + assert output.returncode == 1 From 1cb6717fea2a1ab1bd9606f8bbb6e4091f9e1010 Mon Sep 17 00:00:00 2001 From: yuvipanda Date: Mon, 20 May 2019 09:52:50 -0700 Subject: [PATCH 3/3] Say 'running inside a docker container', not 'plain docker' 'Plain docker' makes no sense --- bootstrap/bootstrap.py | 3 ++- integration-tests/test_bootstrap.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py index adbc9fc..70bdbf8 100644 --- a/bootstrap/bootstrap.py +++ b/bootstrap/bootstrap.py @@ -54,7 +54,8 @@ def validate_host(): print("Systemd is required to run TLJH") # Only fail running inside docker if systemd isn't present if os.path.exists('/.dockerenv'): - print("Running inside a plain docker container isn't supported") + print("Running inside a docker container without systemd isn't supported") + print("We recommend against running a production TLJH instance inside a docker container") print("For local development, see http://tljh.jupyter.org/en/latest/contributing/dev-setup.html") sys.exit(1) diff --git a/integration-tests/test_bootstrap.py b/integration-tests/test_bootstrap.py index a93fc5a..fa02b68 100644 --- a/integration-tests/test_bootstrap.py +++ b/integration-tests/test_bootstrap.py @@ -45,11 +45,12 @@ def test_ubuntu_too_old(): assert output.returncode == 1 -def test_inside_plain_docker(): +def test_inside_no_systemd_docker(): output = run_bootstrap('plain-docker-test', 'ubuntu:18.04') assert output.stdout.strip() == dedent(""" Systemd is required to run TLJH - Running inside a plain docker container isn't supported + Running inside a docker container without systemd isn't supported + We recommend against running a production TLJH instance inside a docker container For local development, see http://tljh.jupyter.org/en/latest/contributing/dev-setup.html """).strip() assert output.returncode == 1