mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-15 14:03:02 +00:00
exercise start/stop race conditions
this doesn’t cover all the edge cases of each possible stage for the races, but it gets the basics covered.
This commit is contained in:
@@ -231,7 +231,10 @@ class UserServerAPIHandler(APIHandler):
|
||||
return
|
||||
|
||||
if not spawner.ready:
|
||||
raise web.HTTPError(400, "%s is not running" % spawner._log_name)
|
||||
raise web.HTTPError(
|
||||
400, "%s is not running %s" %
|
||||
(spawner._log_name, '(pending: %s)' % spawner.pending if spawner.pending else '')
|
||||
)
|
||||
# include notify, so that a server that died is noticed immediately
|
||||
status = yield spawner.poll_and_notify()
|
||||
if status is not None:
|
||||
|
@@ -514,7 +514,7 @@ class BaseHandler(RequestHandler):
|
||||
raise KeyError("User %s has no such spawner %r", user.name, name)
|
||||
spawner = user.spawners[name]
|
||||
if spawner.pending:
|
||||
raise RuntimeError("%s pending %s" % (user_server_name, spawner.pending))
|
||||
raise RuntimeError("%s pending %s" % (spawner._log_name, spawner.pending))
|
||||
# set user._stop_pending before doing anything async
|
||||
# to avoid races
|
||||
spawner._stop_pending = True
|
||||
|
@@ -654,6 +654,50 @@ def test_active_server_limit(app, request):
|
||||
assert counts['pending'] == 0
|
||||
|
||||
|
||||
@mark.gen_test
|
||||
def test_start_stop_race(app, no_patience, slow_spawn):
|
||||
user = add_user(app.db, app, name='panda')
|
||||
spawner = user.spawner
|
||||
# start the server
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='post')
|
||||
assert r.status_code == 202
|
||||
assert spawner.pending == 'spawn'
|
||||
# additional spawns while spawning shouldn't trigger a new spawn
|
||||
with mock.patch.object(spawner, 'start') as m:
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='post')
|
||||
assert r.status_code == 202
|
||||
assert m.call_count == 0
|
||||
|
||||
# stop while spawning is not okay
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='delete')
|
||||
assert r.status_code == 400
|
||||
while not spawner.ready:
|
||||
yield gen.sleep(0.1)
|
||||
|
||||
spawner.delay = 3
|
||||
# stop the spawner
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='delete')
|
||||
assert r.status_code == 202
|
||||
assert spawner.pending == 'stop'
|
||||
# make sure we get past deleting from the proxy
|
||||
yield gen.sleep(1)
|
||||
# additional stops while stopping shouldn't trigger a new stop
|
||||
with mock.patch.object(spawner, 'stop') as m:
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='delete')
|
||||
assert r.status_code == 202
|
||||
assert m.call_count == 0
|
||||
# start while stopping is not allowed
|
||||
with mock.patch.object(spawner, 'start') as m:
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='post')
|
||||
assert r.status_code == 400
|
||||
|
||||
while spawner.active:
|
||||
yield gen.sleep(0.1)
|
||||
# start after stop is okay
|
||||
r = yield api_request(app, 'users', user.name, 'server', method='post')
|
||||
assert r.status_code == 202
|
||||
|
||||
|
||||
@mark.gen_test
|
||||
def test_get_proxy(app):
|
||||
r = yield api_request(app, 'proxy')
|
||||
|
Reference in New Issue
Block a user