support removing named servers

This commit is contained in:
Min RK
2018-09-18 12:48:06 +02:00
parent a1dc73882a
commit e79b43e906
4 changed files with 57 additions and 10 deletions

View File

@@ -217,6 +217,13 @@ paths:
in: path
required: true
type: string
- name: remove
description: |
Whether to fully remove the server, rather than just stop it.
Removing a server deletes things like the state of the stopped server.
in: body
required: false
type: boolean
responses:
'201':
description: The user's notebook named-server has started

View File

@@ -378,29 +378,52 @@ class UserServerAPIHandler(APIHandler):
@admin_or_self
async def delete(self, name, server_name=''):
user = self.find_user(name)
options = self.get_json_body()
remove = (options or {}).get('remove', False)
def _remove_spawner(f=None):
if f and f.exception():
return
self.log.info("Deleting spawner %s", spawner._log_name)
self.db.delete(spawner.orm_spawner)
self.db.commit()
if server_name:
if not self.allow_named_servers:
raise web.HTTPError(400, "Named servers are not enabled.")
if server_name not in user.spawners:
if server_name not in user.orm_spawners:
raise web.HTTPError(404, "%s has no server named '%s'" % (name, server_name))
elif remove:
raise web.HTTPError(400, "Cannot delete the default server")
spawner = user.spawners[server_name]
if spawner.pending == 'stop':
self.log.debug("%s already stopping", spawner._log_name)
self.set_header('Content-Type', 'text/plain')
self.set_status(202)
if remove:
spawner._stop_future.add_done_callback(_remove_spawner)
return
if not spawner.ready:
if spawner.pending:
raise web.HTTPError(
400, "%s is not running %s" %
(spawner._log_name, '(pending: %s)' % spawner.pending if spawner.pending else '')
400, "%s is pending %s, please wait" % (spawner._log_name, spawner.pending)
)
# include notify, so that a server that died is noticed immediately
status = await spawner.poll_and_notify()
if status is not None:
raise web.HTTPError(400, "%s is not running" % spawner._log_name)
await self.stop_single_user(user, server_name)
stop_future = None
if spawner.ready:
# include notify, so that a server that died is noticed immediately
status = await spawner.poll_and_notify()
if status is None:
stop_future = await self.stop_single_user(user, server_name)
if remove:
if stop_future:
stop_future.add_done_callback(_remove_spawner)
else:
_remove_spawner()
status = 202 if spawner._stop_pending else 204
self.set_header('Content-Type', 'text/plain')
self.set_status(status)

View File

@@ -677,7 +677,8 @@ def test_slow_spawn(app, no_patience, slow_spawn):
assert not app_user.spawner._stop_pending
assert app_user.spawner is not None
r = yield api_request(app, 'users', name, 'server', method='delete')
assert r.status_code == 400
# 204 deleted if there's no such server
assert r.status_code == 204
assert app.users.count_active_users()['pending'] == 0
assert app.users.count_active_users()['active'] == 0

View File

@@ -1,4 +1,5 @@
"""Tests for named servers"""
import json
from unittest import mock
import pytest
@@ -134,6 +135,21 @@ def test_delete_named_server(app, named_servers):
'auth_state': None,
'servers': {},
})
# wrapper Spawner is gone
assert servername not in user.spawners
# low-level record still exists
assert servername in user.orm_spawners
r = yield api_request(
app, 'users', username, 'servers', servername,
method='delete',
data=json.dumps({'remove': True}),
)
r.raise_for_status()
assert r.status_code == 204
# low-level record is now removes
assert servername not in user.orm_spawners
@pytest.mark.gen_test
def test_named_server_disabled(app):