diff --git a/dev-requirements.txt b/dev-requirements.txt index 9cf8484..fd93238 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,3 +1,4 @@ pytest pytest-cov codecov +pytoml diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..b48fd4a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,24 @@ +"""pytest fixtures""" +from importlib import reload +import os +import types +from unittest import mock + +import pytest + +import tljh + + +@pytest.fixture +def tljh_dir(tmpdir): + """Fixture for setting up a temporary tljh dir""" + tljh_dir = str(tmpdir.join("tljh").mkdir()) + with mock.patch.dict(os.environ, {"TLJH_INSTALL_PREFIX": tljh_dir}): + reload(tljh) + for name in dir(tljh): + mod = getattr(tljh, name) + if isinstance(mod, types.ModuleType) and mod.__name__.startswith('tljh.'): + reload(mod) + assert tljh.config.INSTALL_PREFIX == tljh_dir + os.makedirs(tljh.config.STATE_DIR) + yield tljh_dir diff --git a/tests/test_config.py b/tests/test_config.py index aed0f49..f84582a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,7 +2,6 @@ Test configuration commandline tools """ -from importlib import reload import os import tempfile from unittest import mock @@ -12,21 +11,6 @@ import pytest from tljh import config, configurer -@pytest.fixture -def tljh_dir(tmpdir): - """Fixture for setting up a temporary tljh dir""" - tljh_dir = str(tmpdir.join("tljh").mkdir()) - with mock.patch.dict( - os.environ, - {"TLJH_INSTALL_PREFIX": tljh_dir} - ): - reload(config) - reload(configurer) - assert config.INSTALL_PREFIX == tljh_dir - os.makedirs(config.STATE_DIR) - yield tljh_dir - - def test_set_no_mutate(): conf = {} diff --git a/tests/test_traefik.py b/tests/test_traefik.py new file mode 100644 index 0000000..0d811ec --- /dev/null +++ b/tests/test_traefik.py @@ -0,0 +1,97 @@ +"""Test traefik configuration""" +import os + +import pytoml as toml + +from tljh import config +from tljh import traefik + + +def test_download_traefik(tmpdir): + traefik_bin = tmpdir.mkdir("bin").join("traefik") + traefik.ensure_traefik_binary(str(tmpdir)) + assert traefik_bin.exists() + # ignore higher-order permission bits, only verify ugo permissions + assert (traefik_bin.stat().mode & 0o777) == 0o755 + + +def test_default_config(tmpdir, tljh_dir): + state_dir = tmpdir.mkdir("state") + traefik.ensure_traefik_config(str(state_dir)) + assert state_dir.join("traefik.toml").exists() + traefik_toml = os.path.join(state_dir, "traefik.toml") + with open(traefik_toml) as f: + toml_cfg = f.read() + # print config for debugging on failure + print(config.CONFIG_FILE) + print(toml_cfg) + cfg = toml.loads(toml_cfg) + assert cfg["defaultEntryPoints"] == ["http"] + assert cfg["entryPoints"] == {"http": {"address": ":80"}} + assert cfg["frontends"] == { + "jupyterhub": {"backend": "jupyterhub", "passHostHeader": True} + } + assert cfg["backends"] == { + "jupyterhub": {"servers": {"chp": {"url": "http://127.0.0.1:15003"}}} + } + + +def test_letsencrypt_config(tljh_dir): + state_dir = config.STATE_DIR + config.set_config_value(config.CONFIG_FILE, "https.enabled", True) + config.set_config_value( + config.CONFIG_FILE, "https.letsencrypt.email", "fake@jupyter.org" + ) + config.set_config_value( + config.CONFIG_FILE, "https.letsencrypt.domains", ["testing.jovyan.org"] + ) + traefik.ensure_traefik_config(str(state_dir)) + traefik_toml = os.path.join(state_dir, "traefik.toml") + with open(traefik_toml) as f: + toml_cfg = f.read() + # print config for debugging on failure + print(config.CONFIG_FILE) + print(toml_cfg) + cfg = toml.loads(toml_cfg) + assert cfg["defaultEntryPoints"] == ["http", "https"] + assert "acme" in cfg + assert cfg["entryPoints"] == { + "http": {"address": ":80", "redirect": {"entryPoint": "https"}}, + "https": {"address": ":443", "backend": "jupyterhub", "tls": {}}, + } + assert cfg["acme"] == { + "email": "fake@jupyter.org", + "storage": "acme.json", + "entryPoint": "https", + "httpChallenge": {"entryPoint": "http"}, + "domains": [{"main": "testing.jovyan.org"}], + } + + +def test_manual_ssl_config(tljh_dir): + state_dir = config.STATE_DIR + config.set_config_value(config.CONFIG_FILE, "https.enabled", True) + config.set_config_value(config.CONFIG_FILE, "https.tls.key", "/path/to/ssl.key") + config.set_config_value(config.CONFIG_FILE, "https.tls.cert", "/path/to/ssl.cert") + traefik.ensure_traefik_config(str(state_dir)) + traefik_toml = os.path.join(state_dir, "traefik.toml") + with open(traefik_toml) as f: + toml_cfg = f.read() + # print config for debugging on failure + print(config.CONFIG_FILE) + print(toml_cfg) + cfg = toml.loads(toml_cfg) + assert cfg["defaultEntryPoints"] == ["http", "https"] + assert "acme" not in cfg + assert cfg["entryPoints"] == { + "http": {"address": ":80", "redirect": {"entryPoint": "https"}}, + "https": { + "address": ":443", + "backend": "jupyterhub", + "tls": { + "certificates": [ + {"certFile": "/path/to/ssl.cert", "keyFile": "/path/to/ssl.key"} + ] + }, + }, + } diff --git a/tljh/traefik.py b/tljh/traefik.py index cf10ecc..6fee96f 100644 --- a/tljh/traefik.py +++ b/tljh/traefik.py @@ -3,7 +3,7 @@ import hashlib import os from urllib.request import urlretrieve -from jinja2 import Environment, Template +from jinja2 import Template from tljh.configurer import load_config