API: pass 'server_state' through to admin clients only

- This will allow, for example, cull_idle_servers to be more
  intelligent when culling servers.
- This is only given to admin API users, because we don't know if all
  spawners expect their state to be made available to users.
This commit is contained in:
Richard Darst
2018-04-18 16:00:49 +03:00
parent bd816310cb
commit 2344d696ca
5 changed files with 14 additions and 5 deletions

View File

@@ -652,6 +652,9 @@ definitions:
type: string type: string
format: date-time format: date-time
description: UTC timestamp last-seen activity on this server. description: UTC timestamp last-seen activity on this server.
state:
type: object
description: Arbitrary internal state from this server's spawner. Only available on the hub's users list or get-user-by-name method, and only if a hub admin. None otherwise.
Group: Group:
type: object type: object
properties: properties:

View File

@@ -90,7 +90,7 @@ class APIHandler(BaseHandler):
'message': message or status_message, 'message': message or status_message,
})) }))
def server_model(self, spawner): def server_model(self, spawner, include_state=False):
"""Get the JSON model for a Spawner""" """Get the JSON model for a Spawner"""
return { return {
'name': spawner.name, 'name': spawner.name,
@@ -98,6 +98,7 @@ class APIHandler(BaseHandler):
'started': isoformat(spawner.orm_spawner.started), 'started': isoformat(spawner.orm_spawner.started),
'pending': spawner.pending, 'pending': spawner.pending,
'ready': spawner.ready, 'ready': spawner.ready,
'state': spawner.get_state() if include_state else None,
'url': url_path_join(spawner.user.url, spawner.name, '/'), 'url': url_path_join(spawner.user.url, spawner.name, '/'),
'progress_url': spawner._progress_url, 'progress_url': spawner._progress_url,
} }
@@ -137,7 +138,7 @@ class APIHandler(BaseHandler):
model.update(extra) model.update(extra)
return model return model
def user_model(self, user, include_servers=False): def user_model(self, user, include_servers=False, include_state=False):
"""Get the JSON model for a User object""" """Get the JSON model for a User object"""
if isinstance(user, orm.User): if isinstance(user, orm.User):
user = self.users[user.id] user = self.users[user.id]
@@ -164,7 +165,7 @@ class APIHandler(BaseHandler):
# include 'active' servers, not just ready # include 'active' servers, not just ready
# (this includes pending events) # (this includes pending events)
if spawner.active: if spawner.active:
servers[name] = self.server_model(spawner) servers[name] = self.server_model(spawner, include_state=include_state)
return model return model
def group_model(self, group): def group_model(self, group):

View File

@@ -36,7 +36,7 @@ class UserListAPIHandler(APIHandler):
@admin_only @admin_only
def get(self): def get(self):
data = [ data = [
self.user_model(u, include_servers=True) self.user_model(u, include_servers=True, include_state=True)
for u in self.db.query(orm.User) for u in self.db.query(orm.User)
] ]
self.write(json.dumps(data)) self.write(json.dumps(data))
@@ -116,7 +116,7 @@ class UserAPIHandler(APIHandler):
@admin_or_self @admin_or_self
async def get(self, name): async def get(self, name):
user = self.find_user(name) user = self.find_user(name)
model = self.user_model(user, include_servers=True) model = self.user_model(user, include_servers=True, include_state=self.get_current_user().admin)
# auth state will only be shown if the requestor is an admin # auth state will only be shown if the requestor is an admin
# this means users can't see their own auth state unless they # this means users can't see their own auth state unless they
# are admins, Hub admins often are also marked as admins so they # are admins, Hub admins often are also marked as admins so they

View File

@@ -207,6 +207,9 @@ def normalize_user(user):
for key in ('started', 'last_activity'): for key in ('started', 'last_activity'):
server[key] = normalize_timestamp(server[key]) server[key] = normalize_timestamp(server[key])
server['progress_url'] = re.sub(r'.*/hub/api', 'PREFIX/hub/api', server['progress_url']) server['progress_url'] = re.sub(r'.*/hub/api', 'PREFIX/hub/api', server['progress_url'])
if (isinstance(server['state'], dict)
and isinstance(server['state'].get('pid', None), int)):
server['state']['pid'] = 0
return user return user
def fill_user(model): def fill_user(model):

View File

@@ -43,6 +43,7 @@ def test_default_server(app, named_servers):
'pending': None, 'pending': None,
'ready': True, 'ready': True,
'progress_url': 'PREFIX/hub/api/users/{}/server/progress'.format(username), 'progress_url': 'PREFIX/hub/api/users/{}/server/progress'.format(username),
'state': {'pid': 0},
}, },
}, },
}) })
@@ -102,6 +103,7 @@ def test_create_named_server(app, named_servers):
'ready': True, 'ready': True,
'progress_url': 'PREFIX/hub/api/users/{}/servers/{}/progress'.format( 'progress_url': 'PREFIX/hub/api/users/{}/servers/{}/progress'.format(
username, servername), username, servername),
'state': {'pid': 0},
} }
for name in [servername] for name in [servername]
}, },