From db3f62b79a068a456c5177426bebd298ed785f28 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 26 Feb 2019 11:57:36 +0100 Subject: [PATCH] try harder to make a useful error message when API requests are made to a not-running server include link to spawn page --- jupyterhub/handlers/base.py | 49 +++++++++++++++++++++++++++------- jupyterhub/tests/test_pages.py | 6 ++--- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py index 2242695e..c54a8c12 100644 --- a/jupyterhub/handlers/base.py +++ b/jupyterhub/handlers/base.py @@ -1159,20 +1159,28 @@ class UserUrlHandler(BaseHandler): Note that this only occurs if bob's server is not already running. """ - def _fail_api_request(self, *args, **kwargs): + def _fail_api_request(self, user_name='', server_name=''): """Fail an API request to a not-running server""" self.log.warning( - "Failing suspected API request to not-running server: %s", self.request.uri + "Failing suspected API request to not-running server: %s", self.request.path ) self.set_status(503) self.set_header("Content-Type", "application/json") + + spawn_url = urlparse(self.request.full_url())._replace(query="") + spawn_path_parts = [self.hub.base_url, "spawn", user_name] + if server_name: + spawn_path_parts.append(server_name) + spawn_url = urlunparse( + spawn_url._replace(path=url_path_join(*spawn_path_parts)) + ) self.write( json.dumps( { "message": ( - "No jupyterhub server running at {}." - " Restart from the Hub home page {}" - ).format(self.request.uri, self.hub.base_url) + "JupyterHub server no longer running at {}." + " Restart the server at {}" + ).format(self.request.path[len(self.hub.base_url) - 1 :], spawn_url) } ) ) @@ -1180,9 +1188,32 @@ class UserUrlHandler(BaseHandler): # fail all non-GET requests with JSON # assuming they are API requests - post = _fail_api_request - patch = _fail_api_request - delete = _fail_api_request + + def non_get(self, user_name, user_path): + """Handle non-get requests + + These all fail with a hopefully informative message + pointing to how to spawn a stopped server + """ + if ( + user_name + and user_path + and self.allow_named_servers + and self.current_user + and user_name == self.current_user.name + ): + server_name = user_path.split('/', 1)[0] + if server_name not in self.current_user.orm_user.orm_spawners: + # no such server, assume default + server_name = '' + else: + server_name = '' + + self._fail_api_request(user_name, server_name) + + post = non_get + patch = non_get + delete = non_get @web.authenticated async def get(self, user_name, user_path): @@ -1280,7 +1311,7 @@ class UserUrlHandler(BaseHandler): self.request.headers.get('Accept', ''), choices=['application/json', 'text/html'], ) == 'application/json' or 'api' in user_path.split('/'): - self._fail_api_request() + self._fail_api_request(user_name, server_name) return pending_url = url_concat( diff --git a/jupyterhub/tests/test_pages.py b/jupyterhub/tests/test_pages.py index c02dd5c1..53dfc444 100644 --- a/jupyterhub/tests/test_pages.py +++ b/jupyterhub/tests/test_pages.py @@ -411,7 +411,7 @@ async def test_login_redirect(app, running, next_url, location): if location: location = ujoin(app.base_url, location) elif running: - location = ujoin(app.base_url, 'user/river/') + location = public_url(app, user) else: # use default url location = ujoin(app.base_url, 'hub/spawn') @@ -697,8 +697,8 @@ async def test_server_not_running_api_request(app): assert r.status_code == 503 assert r.headers["content-type"] == "application/json" message = r.json()['message'] - assert "Hub home page" in message - assert "/user/bees" in message + assert ujoin(app.base_url, "hub/spawn/bees") in message + assert " /user/bees" in message async def test_metrics_no_auth(app):