Add post-spawn hook

This commit is contained in:
James Curtin
2018-05-04 19:56:34 -04:00
parent 10ea92dcea
commit 2f3f8d7826
4 changed files with 32 additions and 3 deletions

View File

@@ -25,8 +25,11 @@ Another use would be to copy initial content, such as tutorial files or referenc
You can define your own bootstrap process by implementing a `pre_spawn_hook` on any spawner. You can define your own bootstrap process by implementing a `pre_spawn_hook` on any spawner.
The Spawner itself is passed as parameter to your hook and you can easily get the contextual information out of the spawning process. The Spawner itself is passed as parameter to your hook and you can easily get the contextual information out of the spawning process.
If you implement a hook, make sure that it is *idempotent*. It will be executed every time Similarly, there may be cases where you would like to clean up after a spawner stops.
a notebook server is spawned to the user. That means you should somehow You may implement a `post_spawn_hook` that is always executed after the spawner stops.
If you implement a hook, make sure that it is *idempotent*. It will be executed every time
a notebook server is spawned to the user. That means you should somehow
ensure that things which should run only once are not running again and again. ensure that things which should run only once are not running again and again.
For example, before you create a directory, check if it exists. For example, before you create a directory, check if it exists.

View File

@@ -2,6 +2,7 @@
# create a directory for the user before the spawner starts # create a directory for the user before the spawner starts
import os import os
import shutil
def create_dir_hook(spawner): def create_dir_hook(spawner):
username = spawner.user.name # get the username username = spawner.user.name # get the username
volume_path = os.path.join('/volumes/jupyterhub', username) volume_path = os.path.join('/volumes/jupyterhub', username)
@@ -10,8 +11,15 @@ def create_dir_hook(spawner):
# now do whatever you think your user needs # now do whatever you think your user needs
# ... # ...
# attach the hook function to the spawner def clean_dir_hook(spawner):
username = spawner.user.name # get the username
temp_path = os.path.join('/volumes/jupyterhub', username, 'temp')
if os.path.exists(temp_path) and os.path.isdir(temp_path):
shutil.rmtree(temp_path)
# attach the hook functions to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook c.Spawner.pre_spawn_hook = create_dir_hook
c.Spawner.post_spawn_hook = clean_dir_hook
# Use the DockerSpawner to serve your users' notebooks # Use the DockerSpawner to serve your users' notebooks
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner' c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'

View File

@@ -515,6 +515,15 @@ class Spawner(LoggingConfigurable):
""" """
).tag(config=True) ).tag(config=True)
post_spawn_hook = Any(
help="""
An optional hook function that you can implement to do work after
the spawner stops.
This can be set independent of any concrete spawner implementation.
"""
).tag(config=True)
def load_state(self, state): def load_state(self, state):
"""Restore state of spawner from database. """Restore state of spawner from database.
@@ -692,6 +701,13 @@ class Spawner(LoggingConfigurable):
if self.pre_spawn_hook: if self.pre_spawn_hook:
return self.pre_spawn_hook(self) return self.pre_spawn_hook(self)
def run_post_spawn_hook(self):
"""Run the post_spawn_hook if defined"""
try:
return self.post_spawn_hook(self)
except Exception:
self.log.exception("post_spawn_hook failed with exception: %s", self)
@property @property
def _progress_url(self): def _progress_url(self):
return self.user.progress_url(self.name) return self.user.progress_url(self.name)

View File

@@ -559,6 +559,8 @@ class User:
finally: finally:
spawner.orm_spawner.started = None spawner.orm_spawner.started = None
self.db.commit() self.db.commit()
# trigger post-spawner hook
await maybe_future(spawner.run_post_spawn_hook())
# trigger post-spawner hook on authenticator # trigger post-spawner hook on authenticator
auth = spawner.authenticator auth = spawner.authenticator
try: try: