mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-18 07:23:00 +00:00
Merge pull request #5146 from agoose77/fix-spawn-429
set HTTP status when spawn via GET params fails
This commit is contained in:
@@ -24,6 +24,7 @@ from ..roles import assign_default_roles
|
||||
from ..scopes import needs_scope
|
||||
from ..user import User
|
||||
from ..utils import (
|
||||
format_exception,
|
||||
isoformat,
|
||||
iterate_until,
|
||||
maybe_future,
|
||||
@@ -865,9 +866,8 @@ class SpawnProgressAPIHandler(APIHandler):
|
||||
failed_event['message'] = "Spawn cancelled"
|
||||
elif f and f.done() and f.exception():
|
||||
exc = f.exception()
|
||||
message = getattr(exc, "jupyterhub_message", str(exc))
|
||||
message, html_message = format_exception(exc)
|
||||
failed_event['message'] = f"Spawn failed: {message}"
|
||||
html_message = getattr(exc, "jupyterhub_html_message", "")
|
||||
if html_message:
|
||||
failed_event['html_message'] = html_message
|
||||
else:
|
||||
@@ -906,9 +906,8 @@ class SpawnProgressAPIHandler(APIHandler):
|
||||
failed_event['message'] = "Spawn cancelled"
|
||||
elif f and f.done() and f.exception():
|
||||
exc = f.exception()
|
||||
message = getattr(exc, "jupyterhub_message", str(exc))
|
||||
message, html_message = format_exception(exc)
|
||||
failed_event['message'] = f"Spawn failed: {message}"
|
||||
html_message = getattr(exc, "jupyterhub_html_message", "")
|
||||
if html_message:
|
||||
failed_event['html_message'] = html_message
|
||||
else:
|
||||
|
@@ -15,7 +15,13 @@ from tornado.httputil import url_concat
|
||||
from .. import __version__, orm
|
||||
from ..metrics import SERVER_POLL_DURATION_SECONDS, ServerPollStatus
|
||||
from ..scopes import describe_raw_scopes, needs_scope
|
||||
from ..utils import maybe_future, url_escape_path, url_path_join, utcnow
|
||||
from ..utils import (
|
||||
format_exception,
|
||||
maybe_future,
|
||||
url_escape_path,
|
||||
url_path_join,
|
||||
utcnow,
|
||||
)
|
||||
from .base import BaseHandler
|
||||
|
||||
|
||||
@@ -92,7 +98,9 @@ class SpawnHandler(BaseHandler):
|
||||
|
||||
default_url = None
|
||||
|
||||
async def _render_form(self, for_user, spawner_options_form, message=''):
|
||||
async def _render_form(
|
||||
self, for_user, spawner_options_form, message='', html_message=''
|
||||
):
|
||||
auth_state = await for_user.get_auth_state()
|
||||
return await self.render_template(
|
||||
'spawn.html',
|
||||
@@ -100,6 +108,7 @@ class SpawnHandler(BaseHandler):
|
||||
auth_state=auth_state,
|
||||
spawner_options_form=spawner_options_form,
|
||||
error_message=message,
|
||||
html_error_message=html_message,
|
||||
url=url_concat(
|
||||
self.request.uri, {"_xsrf": self.xsrf_token.decode('ascii')}
|
||||
),
|
||||
@@ -177,7 +186,6 @@ class SpawnHandler(BaseHandler):
|
||||
await spawner.run_auth_state_hook(auth_state)
|
||||
|
||||
# Try to start server directly when query arguments are passed.
|
||||
error_message = ''
|
||||
query_options = {}
|
||||
for key, byte_list in self.request.query_arguments.items():
|
||||
query_options[key] = [bs.decode('utf8') for bs in byte_list]
|
||||
@@ -185,6 +193,8 @@ class SpawnHandler(BaseHandler):
|
||||
# 'next' is reserved argument for redirect after spawn
|
||||
query_options.pop('next', None)
|
||||
|
||||
spawn_exc = None
|
||||
|
||||
if len(query_options) > 0:
|
||||
try:
|
||||
self.log.debug(
|
||||
@@ -200,16 +210,31 @@ class SpawnHandler(BaseHandler):
|
||||
"Failed to spawn single-user server with query arguments",
|
||||
exc_info=True,
|
||||
)
|
||||
error_message = str(e)
|
||||
spawn_exc = e
|
||||
# fallback to behavior without failing query arguments
|
||||
|
||||
spawner_options_form = await spawner.get_options_form()
|
||||
if spawner_options_form:
|
||||
self.log.debug("Serving options form for %s", spawner._log_name)
|
||||
|
||||
# Explicitly catch HTTPError and report them to the client
|
||||
# This may need scoping to particular error codes.
|
||||
if isinstance(spawn_exc, web.HTTPError):
|
||||
self.set_status(spawn_exc.status_code)
|
||||
|
||||
for name, value in spawn_exc.headers.items():
|
||||
self.set_header(name, value)
|
||||
|
||||
if spawn_exc:
|
||||
error_message, error_html_message = format_exception(spawn_exc)
|
||||
else:
|
||||
error_message = error_html_message = None
|
||||
|
||||
form = await self._render_form(
|
||||
for_user=user,
|
||||
spawner_options_form=spawner_options_form,
|
||||
message=error_message,
|
||||
html_message=error_html_message,
|
||||
)
|
||||
self.finish(form)
|
||||
else:
|
||||
@@ -265,9 +290,23 @@ class SpawnHandler(BaseHandler):
|
||||
self.log.error(
|
||||
"Failed to spawn single-user server with form", exc_info=True
|
||||
)
|
||||
|
||||
# Explicitly catch HTTPError and report them to the client
|
||||
# This may need scoping to particular error codes.
|
||||
if isinstance(e, web.HTTPError):
|
||||
self.set_status(e.status_code)
|
||||
|
||||
for name, value in e.headers.items():
|
||||
self.set_header(name, value)
|
||||
|
||||
error_message, error_html_message = format_exception(e)
|
||||
|
||||
spawner_options_form = await user.spawner.get_options_form()
|
||||
form = await self._render_form(
|
||||
for_user=user, spawner_options_form=spawner_options_form, message=str(e)
|
||||
for_user=user,
|
||||
spawner_options_form=spawner_options_form,
|
||||
message=error_message,
|
||||
html_message=error_html_message,
|
||||
)
|
||||
self.finish(form)
|
||||
return
|
||||
@@ -379,6 +418,8 @@ class SpawnPendingHandler(BaseHandler):
|
||||
if isinstance(exc, web.HTTPError):
|
||||
status_code = exc.status_code
|
||||
self.set_status(status_code)
|
||||
|
||||
message, html_message = format_exception(exc, only_jupyterhub=True)
|
||||
html = await self.render_template(
|
||||
"not_running.html",
|
||||
user=user,
|
||||
@@ -386,8 +427,8 @@ class SpawnPendingHandler(BaseHandler):
|
||||
server_name=server_name,
|
||||
spawn_url=spawn_url,
|
||||
failed=True,
|
||||
failed_html_message=getattr(exc, 'jupyterhub_html_message', ''),
|
||||
failed_message=getattr(exc, 'jupyterhub_message', ''),
|
||||
failed_html_message=html_message,
|
||||
failed_message=message,
|
||||
exception=exc,
|
||||
)
|
||||
self.finish(html)
|
||||
|
@@ -984,3 +984,13 @@ def fmt_ip_url(ip):
|
||||
if ":" in ip:
|
||||
return f"[{ip}]"
|
||||
return ip
|
||||
|
||||
|
||||
def format_exception(exc, *, only_jupyterhub=False):
|
||||
"""
|
||||
Format an exception into a text string and HTML pair.
|
||||
"""
|
||||
default_message = None if only_jupyterhub else str(exc)
|
||||
return getattr(exc, "jupyterhub_message", default_message), getattr(
|
||||
exc, "jupyterhub_html_message", None
|
||||
)
|
||||
|
@@ -14,7 +14,11 @@
|
||||
{% if for_user and user.name != for_user.name -%}
|
||||
<p>Spawning server for {{ for_user.name }}</p>
|
||||
{% endif -%}
|
||||
{% if error_message -%}<p class="spawn-error-msg alert alert-danger">Error: {{ error_message }}</p>{% endif %}
|
||||
{% if error_message %}
|
||||
<p class="spawn-error-msg alert alert-danger">Error: {{ error_message }}</p>
|
||||
{% elif error_html_message %}
|
||||
<p class="spawn-error-msg alert alert-danger">{{ error_html_message | safe }}</p>
|
||||
{% endif %}
|
||||
<form enctype="multipart/form-data"
|
||||
id="spawn_form"
|
||||
action="{{ url | safe }}"
|
||||
|
Reference in New Issue
Block a user