From 4401cdc16a87df22ed9ab467a15bb8d44b4cd6e8 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 20 Apr 2018 13:05:08 +0200 Subject: [PATCH] Always have `.servers` model on users rather than only when named_servers is enabled. Put new-in-0.9 state there, rather than continuing to add server fields to top-level user model. --- docs/rest-api.yml | 44 ++++++++++++++++++++++++-- jupyterhub/apihandlers/base.py | 19 +++++------ jupyterhub/tests/test_api.py | 11 ++----- jupyterhub/tests/test_named_servers.py | 3 +- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/docs/rest-api.yml b/docs/rest-api.yml index 8c8debbb..bc1a1db7 100644 --- a/docs/rest-api.yml +++ b/docs/rest-api.yml @@ -3,7 +3,7 @@ swagger: '2.0' info: title: JupyterHub description: The REST API for JupyterHub - version: 0.8.0dev + version: 0.9.0dev license: name: BSD-3-Clause schemes: @@ -606,12 +606,52 @@ definitions: description: The user's notebook server's base URL, if running; null if not. pending: type: string - enum: ["spawn", "stop"] + enum: ["spawn", "stop", null] description: The currently pending action, if any last_activity: type: string format: date-time description: Timestamp of last-seen activity from the user + servers: + type: object + description: The active servers for this user. + items: + schema: + $ref: '#/definitions/Server' + Server: + type: object + properties: + name: + type: string + description: The server's name. The user's default server has an empty name ('') + ready: + type: boolean + description: | + Whether the server is ready for traffic. + Will always be false when any transition is pending. + pending: + type: string + enum: ["spawn", "stop", null] + description: | + The currently pending action, if any. + A server is not ready if an action is pending. + url: + type: string + description: | + The URL where the server can be accessed + (typically /user/:name/:server.name/). + progress_url: + type: string + description: | + The URL for an event-stream to retrieve events during a spawn. + started: + type: string + format: date-time + description: UTC timestamp when the server was last started. + last_activity: + type: string + format: date-time + description: UTC timestamp last-seen activity on this server. Group: type: object properties: diff --git a/jupyterhub/apihandlers/base.py b/jupyterhub/apihandlers/base.py index 0f401889..1483f2b7 100644 --- a/jupyterhub/apihandlers/base.py +++ b/jupyterhub/apihandlers/base.py @@ -97,6 +97,7 @@ class APIHandler(BaseHandler): 'last_activity': isoformat(spawner.orm_spawner.last_activity), 'started': isoformat(spawner.orm_spawner.started), 'pending': spawner.pending or None, + 'ready': spawner.ready, 'url': url_path_join(spawner.user.url, spawner.name, '/'), 'progress_url': spawner._progress_url, } @@ -147,23 +148,19 @@ class APIHandler(BaseHandler): 'admin': user.admin, 'groups': [ g.name for g in user.groups ], 'server': user.url if user.running else None, - 'progress_url': user.progress_url(''), 'pending': None, 'created': isoformat(user.created), - 'started': None, 'last_activity': isoformat(user.last_activity), } if '' in user.spawners: - server_model = self.server_model(user.spawners['']) - # copy some values from the default server to the user model - for key in ('started', 'pending'): - model[key] = server_model[key] + model['pending'] = user.spawners[''].pending - if self.allow_named_servers: - servers = model['servers'] = {} - for name, spawner in user.spawners.items(): - if spawner.ready: - servers[name] = self.server_model(spawner) + servers = model['servers'] = {} + for name, spawner in user.spawners.items(): + # include 'active' servers, not just ready + # (this includes pending events) + if spawner.active: + servers[name] = self.server_model(spawner) return model def group_model(self, group): diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py index d74d5067..3112413e 100644 --- a/jupyterhub/tests/test_api.py +++ b/jupyterhub/tests/test_api.py @@ -200,14 +200,8 @@ def normalize_user(user): smooths out user model with things like timestamps for easier comparison """ - for key in ('created', 'last_activity', 'started'): + for key in ('created', 'last_activity'): user[key] = normalize_timestamp(user[key]) - if user['progress_url']: - user['progress_url'] = re.sub( - r'.*/hub/api', - 'PREFIX/hub/api', - user['progress_url'], - ) if 'servers' in user: for server in user['servers'].values(): for key in ('started', 'last_activity'): @@ -228,8 +222,7 @@ def fill_user(model): model.setdefault('pending', None) model.setdefault('created', TIMESTAMP) model.setdefault('last_activity', TIMESTAMP) - model.setdefault('started', None) - model.setdefault('progress_url', 'PREFIX/hub/api/users/{name}/server/progress'.format(**model)) + model.setdefault('servers', {}) return model diff --git a/jupyterhub/tests/test_named_servers.py b/jupyterhub/tests/test_named_servers.py index f6c1e757..04f07410 100644 --- a/jupyterhub/tests/test_named_servers.py +++ b/jupyterhub/tests/test_named_servers.py @@ -34,7 +34,6 @@ def test_default_server(app, named_servers): 'name': username, 'auth_state': None, 'server': user.url, - 'started': TIMESTAMP, 'servers': { '': { 'name': '', @@ -42,6 +41,7 @@ def test_default_server(app, named_servers): 'last_activity': TIMESTAMP, 'url': user.url, 'pending': None, + 'ready': True, 'progress_url': 'PREFIX/hub/api/users/{}/server/progress'.format(username), }, }, @@ -99,6 +99,7 @@ def test_create_named_server(app, named_servers): 'last_activity': TIMESTAMP, 'url': url_path_join(user.url, name, '/'), 'pending': None, + 'ready': True, 'progress_url': 'PREFIX/hub/api/users/{}/servers/{}/progress'.format( username, servername), }