Skip to content

Commit

Permalink
Replace chp with traefik-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgianaElena committed Jan 30, 2019
1 parent 6a0d8bb commit e35911a
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 97 deletions.
2 changes: 1 addition & 1 deletion .circleci/integration-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def show_logs(container_name):
)
run_container_command(
container_name,
'systemctl --no-pager status jupyterhub configurable-http-proxy'
'systemctl --no-pager status jupyterhub traefik'
)

def main():
Expand Down
8 changes: 8 additions & 0 deletions bootstrap/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,20 @@ def main():
'git+https://github.com/jupyterhub/the-littlest-jupyterhub.git'
)

traefik_proxy_repo_path = 'git+https://github.com/jupyterhub/traefik-proxy.git'

subprocess.check_output([
os.path.join(hub_prefix, 'bin', 'pip'),
'install'
] + pip_flags + [tljh_repo_path], stderr=subprocess.STDOUT)
logger.info('Setup tljh package')

subprocess.check_output([
os.path.join(hub_prefix, 'bin', 'pip'),
'install'
] + [traefik_proxy_repo_path], stderr=subprocess.STDOUT)
logger.info('Setup traefik-proxy package')

logger.info('Starting TLJH installer...')
os.execv(
os.path.join(hub_prefix, 'bin', 'python3'),
Expand Down
17 changes: 1 addition & 16 deletions integration-tests/test_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ async def test_user_code_execute():
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'set', 'auth.type', 'dummyauthenticator.DummyAuthenticator')).wait()
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'reload')).wait()

# FIXME: wait for reload to finish & hub to come up
# Should be part of tljh-config reload
await asyncio.sleep(1)

async with User(username, hub_url, partial(login_dummy, password='')) as u:
await u.login()
await u.ensure_server()
Expand All @@ -62,9 +58,6 @@ async def test_user_admin_add():
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'add-item', 'users.admin', username)).wait()
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'reload')).wait()

# FIXME: wait for reload to finish & hub to come up
# Should be part of tljh-config reload
await asyncio.sleep(1)
async with User(username, hub_url, partial(login_dummy, password='')) as u:
await u.login()
await u.ensure_server()
Expand Down Expand Up @@ -92,9 +85,6 @@ async def test_user_admin_remove():
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'add-item', 'users.admin', username)).wait()
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'reload')).wait()

# FIXME: wait for reload to finish & hub to come up
# Should be part of tljh-config reload
await asyncio.sleep(1)
async with User(username, hub_url, partial(login_dummy, password='')) as u:
await u.login()
await u.ensure_server()
Expand All @@ -105,16 +95,14 @@ async def test_user_admin_remove():
# Assert that the user has admin rights
assert f'jupyter-{username}' in grp.getgrnam('jupyterhub-admins').gr_mem


assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'remove-item', 'users.admin', username)).wait()
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'reload')).wait()
await asyncio.sleep(1)

await u.stop_server()
await u.ensure_server()

# Assert that the user does *not* have admin rights
assert f'jupyter-{username}' in grp.getgrnam('jupyterhub-admins').gr_mem
assert f'jupyter-{username}' not in grp.getgrnam('jupyterhub-admins').gr_mem


@pytest.mark.asyncio
Expand All @@ -130,9 +118,6 @@ async def test_long_username():
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'set', 'auth.type', 'dummyauthenticator.DummyAuthenticator')).wait()
assert 0 == await (await asyncio.create_subprocess_exec(*TLJH_CONFIG_PATH, 'reload')).wait()

# FIXME: wait for reload to finish & hub to come up
# Should be part of tljh-config reload
await asyncio.sleep(1)
try:
async with User(username, hub_url, partial(login_dummy, password='')) as u:
await u.login()
Expand Down
11 changes: 7 additions & 4 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def test_add_to_config_zero_level():
'a': ['b']
}


def test_add_to_config_multiple():
conf = {}

Expand Down Expand Up @@ -116,15 +117,17 @@ def test_remove_from_config_error():


def test_reload_hub():
with mock.patch('tljh.systemd.restart_service') as restart_service:
with mock.patch('tljh.systemd.restart_service') as restart_service, mock.patch(
'tljh.systemd.check_service_active'
) as check_active, mock.patch('tljh.systemd.check_hub_ready') as check_ready:
config.reload_component('hub')
assert restart_service.called_with('jupyterhub')
assert check_active.called_with('jupyterhub')


def test_reload_proxy(tljh_dir):
with mock.patch('tljh.systemd.restart_service') as restart_service:
config.reload_component('proxy')
assert restart_service.called_with('configurable-http-proxy')
assert restart_service.called_with('traefik')
assert os.path.exists(os.path.join(config.STATE_DIR, 'traefik.toml'))

Expand All @@ -140,8 +143,8 @@ def test_cli_no_command(capsys):
"arg, value",
[
("true", True),
("FALSE", False),
],
("FALSE", False)
]
)
def test_cli_set_bool(tljh_dir, arg, value):
config.main(["set", "https.enabled", arg])
Expand Down
29 changes: 21 additions & 8 deletions tests/test_traefik.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ def test_default_config(tmpdir, tljh_dir):
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"}}}
assert cfg["entryPoints"] == {
"http": {"address": ":80"},
"auth_api": {
"address": ":8099",
"auth": {
"basic": {"users": ["api_admin:$apr1$eS/j3kum$q/X2khsIEG/bBGsteP.x./"]}
},
},
}


Expand All @@ -57,7 +59,13 @@ def test_letsencrypt_config(tljh_dir):
assert "acme" in cfg
assert cfg["entryPoints"] == {
"http": {"address": ":80", "redirect": {"entryPoint": "https"}},
"https": {"address": ":443", "backend": "jupyterhub", "tls": {}},
"https": {"address": ":443", "tls": {}},
"auth_api": {
"address": ":8099",
"auth": {
"basic": {"users": ["api_admin:$apr1$eS/j3kum$q/X2khsIEG/bBGsteP.x./"]}
},
},
}
assert cfg["acme"] == {
"email": "fake@jupyter.org",
Expand Down Expand Up @@ -87,11 +95,16 @@ def test_manual_ssl_config(tljh_dir):
"http": {"address": ":80", "redirect": {"entryPoint": "https"}},
"https": {
"address": ":443",
"backend": "jupyterhub",
"tls": {
"certificates": [
{"certFile": "/path/to/ssl.cert", "keyFile": "/path/to/ssl.key"}
]
},
},
"auth_api": {
"address": ":8099",
"auth": {
"basic": {"users": ["api_admin:$apr1$eS/j3kum$q/X2khsIEG/bBGsteP.x./"]}
},
},
}
10 changes: 7 additions & 3 deletions tljh/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import re
import sys
import asyncio

from .yaml import yaml

Expand Down Expand Up @@ -85,7 +86,7 @@ def add_item_to_config(config, property_path, value):

def remove_item_from_config(config, property_path, value):
"""
Add an item to a list in config.
Remove an item from a list in config.
"""
path_components = property_path.split('.')

Expand Down Expand Up @@ -183,11 +184,14 @@ def reload_component(component):
from tljh import systemd, traefik
if component == 'hub':
systemd.restart_service('jupyterhub')
# FIXME: Verify hub is back up?
# Ensure hub is back up
while not systemd.check_service_active('jupyterhub'):
asyncio.sleep(1)
while not systemd.check_hub_ready():
asyncio.sleep(1)
print('Hub reload with new configuration complete')
elif component == 'proxy':
traefik.ensure_traefik_config(STATE_DIR)
systemd.restart_service('configurable-http-proxy')
systemd.restart_service('traefik')
print('Proxy reload with new configuration complete')

Expand Down
18 changes: 1 addition & 17 deletions tljh/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@ def ensure_jupyterhub_service(prefix):
with open(os.path.join(HERE, 'systemd-units', 'jupyterhub.service')) as f:
hub_unit_template = f.read()

with open(os.path.join(HERE, 'systemd-units', 'configurable-http-proxy.service')) as f:
proxy_unit_template = f.read()

with open(os.path.join(HERE, 'systemd-units', 'traefik.service')) as f:
traefik_unit_template = f.read()

Expand All @@ -133,28 +130,16 @@ def ensure_jupyterhub_service(prefix):
jupyterhub_config_path=os.path.join(HERE, 'jupyterhub_config.py'),
install_prefix=INSTALL_PREFIX,
)
systemd.install_unit('configurable-http-proxy.service', proxy_unit_template.format(**unit_params))
systemd.install_unit('jupyterhub.service', hub_unit_template.format(**unit_params))
systemd.install_unit('traefik.service', traefik_unit_template.format(**unit_params))
systemd.reload_daemon()

# Set up proxy / hub secret oken if it is not already setup
proxy_secret_path = os.path.join(STATE_DIR, 'configurable-http-proxy.secret')
if not os.path.exists(proxy_secret_path):
with open(proxy_secret_path, 'w') as f:
f.write('CONFIGPROXY_AUTH_TOKEN=' + secrets.token_hex(32))
# If we are changing CONFIGPROXY_AUTH_TOKEN, restart configurable-http-proxy!
systemd.restart_service('configurable-http-proxy')

# Start CHP if it has already not been started
systemd.start_service('configurable-http-proxy')
# If JupyterHub is running, we want to restart it.
systemd.restart_service('jupyterhub')
systemd.restart_service('traefik')

# Mark JupyterHub & CHP to start at boot time
systemd.enable_service('jupyterhub')
systemd.enable_service('configurable-http-proxy')
systemd.enable_service('traefik')


Expand Down Expand Up @@ -275,7 +260,7 @@ def ensure_admins(admins):
yaml.dump(config, f)


def ensure_jupyterhub_running(times=4):
def ensure_jupyterhub_running(times=20):
"""
Ensure that JupyterHub is up and running
Expand Down Expand Up @@ -432,7 +417,6 @@ def main():
logger.info("Setting up JupyterHub...")
ensure_node()
ensure_jupyterhub_package(HUB_ENV_PREFIX)
ensure_chp_package(HUB_ENV_PREFIX)
ensure_jupyterlab_extensions()
ensure_jupyterhub_service(HUB_ENV_PREFIX)
ensure_jupyterhub_running()
Expand Down
9 changes: 6 additions & 3 deletions tljh/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from tljh import configurer, user
from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX, CONFIG_DIR
from tljh.normalize import generate_system_username

from jupyterhub_traefik_proxy import TraefikTomlProxy

class UserCreatingSpawner(SystemdSpawner):
"""
Expand Down Expand Up @@ -43,8 +43,11 @@ def start(self):
# Use a high port so users can try this on machines with a JupyterHub already present
c.JupyterHub.hub_port = 15001

c.ConfigurableHTTPProxy.should_start = False
c.ConfigurableHTTPProxy.api_url = 'http://127.0.0.1:15002'
c.TraefikTomlProxy.should_start = False
c.TraefikTomlProxy.traefik_api_password = "admin"
c.TraefikTomlProxy.traefik_api_username = "api_admin"
c.TraefikTomlProxy.toml_dynamic_config_file = "/opt/tljh/state/rules.toml"
c.JupyterHub.proxy_class = TraefikTomlProxy

c.SystemdSpawner.extra_paths = [os.path.join(USER_ENV_PREFIX, 'bin')]
c.SystemdSpawner.default_shell = '/bin/bash'
Expand Down
27 changes: 0 additions & 27 deletions tljh/systemd-units/configurable-http-proxy.service

This file was deleted.

7 changes: 3 additions & 4 deletions tljh/systemd-units/jupyterhub.service
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Template file for JupyterHub systemd service
# Uses simple string.format() for 'templating'
[Unit]
# CHP must have successfully started *before* we launch JupyterHub
Requires=configurable-http-proxy.service
After=configurable-http-proxy.service
# Traefik must have successfully started *before* we launch JupyterHub
Requires=traefik.service
After=traefik.service

[Service]
User=root
Expand All @@ -17,7 +17,6 @@ PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
# Source CONFIGPROXY_AUTH_TOKEN from here!
EnvironmentFile={install_prefix}/state/configurable-http-proxy.secret
Environment=TLJH_INSTALL_PREFIX={install_prefix}
ExecStart={python_interpreter_path} -m jupyterhub.app -f {jupyterhub_config_path}

Expand Down
2 changes: 1 addition & 1 deletion tljh/systemd-units/traefik.service
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ After=network.target
[Service]
User=root
Restart=always
# process only needs to write state/acme.json file, no other files
ProtectHome=tmpfs
ProtectSystem=strict
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ReadWritePaths={install_prefix}/state/rules.toml
ReadWritePaths={install_prefix}/state/acme.json
WorkingDirectory={install_prefix}/state
ExecStart={install_prefix}/hub/bin/traefik \
Expand Down

0 comments on commit e35911a

Please sign in to comment.