I would like to render a custom error depending on the return status of a command call in a pre-spawn hook.
Does anyone have experience with something like this or similar? It seems to me like I should be using something like what’s in handlers/pages.py but I’m not entirely sure.
from tornado import web
def pre_spawn_hook(spawner):
raise web.HTTPError(403)
c.Spawner.pre_spawn_hook = pre_spawn_hook
This raised a 403 error when the user tried to start their server. Is this what you are looking for? You can use any of the HTTP error codes here.
Do you need to style that page too? I’m not quite sure exactly how to do that yet, but I’m guessing you can do so by modifying the Jinja templates for those errors. https://jupyterhub.readthedocs.io/en/stable/reference/templates.html has some pointers on how to do that, although I haven’t tried to do it myself.
That is pretty close to what I am looking for, but I would like to include some custom error text as well (I made a bit of progress since posting, so I hadn’t gotten to this point then).
Some light Googling has suggested that I define a small basic handler class and set the message in there - I plan to try that Monday morning.
If it’s just a little text, @yuvipanda’s proposal should work, but in testing I found a bug where errors raised in pre_spawn_hook can end up wrapped in a HTTPError(500) (not always), so it’s hard to get access to what you need here without some changes in JupyterHub. We should have an Issue about letting HTTPErrors propagate up here.
For the template, if you create a directory templates with a single file in it: templates/error.html:
The else...super() bit means do what the original template would have done if your .my_message is not defined.
and then have in jupyterhub_config.py, register your templates and a hook to raise the error for testing:
# tell jupyterhub to find your template
c.JupyterHub.template_paths = [my_template_dir]
# pre_spawn_hook that raises our special error
def pre_spawn_hook(spawner):
e = web.HTTPError(500, "this should show up, but won't")
e.my_message = "This will show up"
raise e
c.Spawner.pre_spawn_hook = pre_spawn_hook
We should have an Issue open about not re-wrapping HTTPErrors raised, only other errors. That would make it possible to do what @yuvipanda proposed, which could be further customized with a custom error template. It might be possible now, but it would require some extra fiddly bits.
I failed to follow up on this (oops!), and of course am now following up with a question.
We tried a pre-spawn hook to the effect of:
async def setup(spawner):
async with asyncssh.connect(remote_host) as conn:
result = await conn.run(<cmd>)
retcode = result.exit_status
if retcode:
e = web.HTTPError(507,reason="Insufficient Storage")
e.my_message = "There is insufficient space."
raise e
c.Spawner.pre_spawn_hook = setup
In general, users for whom retcode=0 are able to login successfully.
For users that have retcode=1, the error message displays as expected upon their first login/spawn attempt. However, when a user makes a change such that retcode should then equal 0, they are still seeing the 507 error and message. This persists through stopping the server and also through the user logging out and then back in.
Does the pre-spawn hook not always happen before each individual spawn attempt? It’s not clear to me why this error seems to be “sticking”.
It should happen on each actual request to spawn, but if users are just visiting /user/theirname, then that will not trigger a new spawn if the most recent spawn request failed. This is to avoid problems where:
visits /user/theirname triggers spawn
spawn fails
refreshes /user/theirname triggers new spawn instead of showing the error
Because it is not known if a visit to /user/:name is refreshing the page from the failed request or a new one.
When a spawn fails, the user must go to the main page and click ‘start my server’ to request a new launch, which should always request a new spawn and call pre_spawn_hook.
That makes a lot of sense, thanks. Is there a variable or attribute to get the entire home URL in the jupyterhub_config.py file?
I was able to put a redirect to the home page in with the URL hardcoded (and I see how it’s done in the base handler), but I’m having difficulty getting that whole URL in the pre-spawn hook.
Something you could have a look at I’ve seen a few interesting and unique custom 404’s. I don’t understand why do more web owners not do this, it’s not hard. Look at https://under-the-open-sky.com/404/ You get the point. When this pops up you don’t seem to feel that bad.
Workaround proposed by @minrk does not work for me.
Any exception e.g. tornado.web.HTTPError or basic RuntimeError raised in pre_spawn_hook leads to this message:
From the message I got that not_running.html template is used instead of error.html
Any ideas how to show up custom error message produced in pre_spawn_hook?