diff --git a/integration-tests/plugins/simplest/tljh_simplest.py b/integration-tests/plugins/simplest/tljh_simplest.py index 3c6a3f7..f77f19b 100644 --- a/integration-tests/plugins/simplest/tljh_simplest.py +++ b/integration-tests/plugins/simplest/tljh_simplest.py @@ -46,3 +46,9 @@ def tljh_custom_jupyterhub_config(c): def tljh_post_install(): with open('test_post_install', 'w') as f: f.write('123456789') + + +@hookimpl +def tljh_new_user_create(username): + with open('test_new_user_create', 'w') as f: + f.write("a new userfile") diff --git a/integration-tests/test_simplest_plugin.py b/integration-tests/test_simplest_plugin.py index bb07a83..d2f973c 100644 --- a/integration-tests/test_simplest_plugin.py +++ b/integration-tests/test_simplest_plugin.py @@ -72,3 +72,13 @@ def test_post_install_hook(): content = f.read() assert content == "123456789" + + +def test_tljh_new_user_create(): + """ + Test that plugin receives username as arg + """ + with open("test_new_user_create") as f: + content = f.read() + + assert content == "a new userfile" diff --git a/tljh/hooks.py b/tljh/hooks.py index d59f246..b8c7808 100644 --- a/tljh/hooks.py +++ b/tljh/hooks.py @@ -66,6 +66,14 @@ def tljh_post_install(): Post install script to be executed after installation and after all the other hooks. + This can be arbitrary Python code. + """ + pass + +@hookspec +def tljh_new_user_create(username): + """ + Script to be executed after a new user has been added. This can be arbitrary Python code. """ pass \ No newline at end of file diff --git a/tljh/jupyterhub_config.py b/tljh/jupyterhub_config.py index 51455dc..38fbaa9 100644 --- a/tljh/jupyterhub_config.py +++ b/tljh/jupyterhub_config.py @@ -4,13 +4,12 @@ JupyterHub config for the littlest jupyterhub. from glob import glob import os -import pluggy from systemdspawner import SystemdSpawner -from tljh import configurer, user, hooks +from tljh import configurer, user from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX, CONFIG_DIR from tljh.normalize import generate_system_username -from tljh.yaml import yaml +from tljh.utils import get_plugin_manager from jupyterhub_traefik_proxy import TraefikTomlProxy from traitlets import Dict, Unicode, List @@ -68,11 +67,8 @@ configurer.apply_config(tljh_config, c) # Let TLJH hooks modify `c` if they want -# Set up plugin infrastructure -pm = pluggy.PluginManager('tljh') -pm.add_hookspecs(hooks) -pm.load_setuptools_entrypoints('tljh') # Call our custom configuration plugin +pm = get_plugin_manager() pm.hook.tljh_custom_jupyterhub_config(c=c) # Load arbitrary .py config files if they exist. diff --git a/tljh/user.py b/tljh/user.py index ef946e3..d4daf1f 100644 --- a/tljh/user.py +++ b/tljh/user.py @@ -3,11 +3,14 @@ User management for tljh. Supports minimal user & group management """ -import pwd import grp +import pwd import subprocess from os.path import expanduser +# Set up plugin infrastructure +from tljh.utils import get_plugin_manager + def ensure_user(username): """ @@ -30,10 +33,13 @@ def ensure_user(username): subprocess.check_call([ 'chmod', - 'o-rwx', + 'o-rwx', expanduser('~{username}'.format(username=username)) ]) + pm = get_plugin_manager() + pm.hook.tljh_new_user_create(username=username) + def remove_user(username): """ diff --git a/tljh/utils.py b/tljh/utils.py index 395d395..460b24d 100644 --- a/tljh/utils.py +++ b/tljh/utils.py @@ -1,11 +1,15 @@ """ -Miscelaneous functions useful in at least two places unrelated to each other +Miscellaneous functions useful in at least two places unrelated to each other """ -import subprocess import logging - +import subprocess # Copied into bootstrap/bootstrap.py. Make sure these two copies are exactly the same! +import pluggy + +from tljh import hooks + + def run_subprocess(cmd, *args, **kwargs): """ Run given cmd with smart output behavior. @@ -33,4 +37,16 @@ def run_subprocess(cmd, *args, **kwargs): )) # This produces multi line log output, unfortunately. Not sure how to fix. # For now, prioritizing human readability over machine readability. - logger.debug(proc.stdout.decode()) \ No newline at end of file + logger.debug(proc.stdout.decode()) + + +def get_plugin_manager(): + """ + Return plugin manager instance + """ + # Set up plugin infrastructure + pm = pluggy.PluginManager('tljh') + pm.add_hookspecs(hooks) + pm.load_setuptools_entrypoints('tljh') + + return pm