mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-15 22:13:00 +00:00
move some state to Spawner
now that there are more than one per user
This commit is contained in:
@@ -97,9 +97,9 @@ class APIHandler(BaseHandler):
|
|||||||
'pending': None,
|
'pending': None,
|
||||||
'last_activity': user.last_activity.isoformat(),
|
'last_activity': user.last_activity.isoformat(),
|
||||||
}
|
}
|
||||||
if user.spawn_pending:
|
if user.spawners['']._spawn_pending:
|
||||||
model['pending'] = 'spawn'
|
model['pending'] = 'spawn'
|
||||||
elif user.stop_pending:
|
elif user.spawners['']._stop_pending:
|
||||||
model['pending'] = 'stop'
|
model['pending'] = 'stop'
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
@@ -146,11 +146,11 @@ class UserAPIHandler(APIHandler):
|
|||||||
raise web.HTTPError(404)
|
raise web.HTTPError(404)
|
||||||
if user.name == self.get_current_user().name:
|
if user.name == self.get_current_user().name:
|
||||||
raise web.HTTPError(400, "Cannot delete yourself!")
|
raise web.HTTPError(400, "Cannot delete yourself!")
|
||||||
if user.stop_pending:
|
if user.spawner._stop_pending:
|
||||||
raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
|
raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
|
||||||
if user.running:
|
if user.running:
|
||||||
yield self.stop_single_user(user)
|
yield self.stop_single_user(user)
|
||||||
if user.stop_pending:
|
if user.spawner._stop_pending:
|
||||||
raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
|
raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
|
||||||
|
|
||||||
yield gen.maybe_future(self.authenticator.delete_user(user))
|
yield gen.maybe_future(self.authenticator.delete_user(user))
|
||||||
@@ -193,14 +193,14 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
|
|
||||||
options = self.get_json_body()
|
options = self.get_json_body()
|
||||||
yield self.spawn_single_user(user, options=options)
|
yield self.spawn_single_user(user, options=options)
|
||||||
status = 202 if user.spawn_pending else 201
|
status = 202 if user.spawner._spawn_pending else 201
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
@admin_or_self
|
@admin_or_self
|
||||||
def delete(self, name):
|
def delete(self, name):
|
||||||
user = self.find_user(name)
|
user = self.find_user(name)
|
||||||
if user.stop_pending:
|
if user.spawner._stop_pending:
|
||||||
self.set_status(202)
|
self.set_status(202)
|
||||||
return
|
return
|
||||||
if not user.running:
|
if not user.running:
|
||||||
@@ -210,7 +210,7 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
if status is not None:
|
if status is not None:
|
||||||
raise web.HTTPError(400, "%s's server is not running" % name)
|
raise web.HTTPError(400, "%s's server is not running" % name)
|
||||||
yield self.stop_single_user(user)
|
yield self.stop_single_user(user)
|
||||||
status = 202 if user.stop_pending else 204
|
status = 202 if user.spawner._stop_pending else 204
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
|
|
||||||
|
|
||||||
@@ -221,10 +221,11 @@ class UserCreateNamedServerAPIHandler(APIHandler):
|
|||||||
"""
|
"""
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
@admin_or_self
|
@admin_or_self
|
||||||
def post(self, name):
|
def post(self, name, server_name=''):
|
||||||
user = self.find_user(name)
|
user = self.find_user(name)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise web.HTTPError(404, "No such user %r" % name)
|
raise web.HTTPError(404, "No such user %r" % name)
|
||||||
|
|
||||||
#if user.running:
|
#if user.running:
|
||||||
# # include notify, so that a server that died is noticed immediately
|
# # include notify, so that a server that died is noticed immediately
|
||||||
# state = yield user.spawner.poll_and_notify()
|
# state = yield user.spawner.poll_and_notify()
|
||||||
@@ -232,8 +233,8 @@ class UserCreateNamedServerAPIHandler(APIHandler):
|
|||||||
# raise web.HTTPError(400, "%s's server is already running" % name)
|
# raise web.HTTPError(400, "%s's server is already running" % name)
|
||||||
|
|
||||||
options = self.get_json_body()
|
options = self.get_json_body()
|
||||||
yield self.spawn_single_user(user, options=options)
|
yield self.spawn_single_user(user, server_name, options=options)
|
||||||
status = 202 if user.spawn_pending else 201
|
status = 202 if user.spawner._spawn_pending else 201
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
|
|
||||||
|
|
||||||
@@ -248,17 +249,18 @@ class UserDeleteNamedServerAPIHandler(APIHandler):
|
|||||||
@admin_or_self
|
@admin_or_self
|
||||||
def delete(self, name, server_name):
|
def delete(self, name, server_name):
|
||||||
user = self.find_user(name)
|
user = self.find_user(name)
|
||||||
if user.stop_pending:
|
spawner = user.spawners[server_name]
|
||||||
|
if spawner._stop_pending:
|
||||||
self.set_status(202)
|
self.set_status(202)
|
||||||
return
|
return
|
||||||
#if not user.running:
|
#if not user.running:
|
||||||
# raise web.HTTPError(400, "%s's server is not running" % name)
|
# raise web.HTTPError(400, "%s's server is not running" % name)
|
||||||
# include notify, so that a server that died is noticed immediately
|
# include notify, so that a server that died is noticed immediately
|
||||||
status = yield user.spawner.poll_and_notify()
|
status = yield spawner.poll_and_notify()
|
||||||
if status is not None:
|
if status is not None:
|
||||||
raise web.HTTPError(400, "%s's server is not running" % name)
|
raise web.HTTPError(400, "%s's server is not running" % name)
|
||||||
yield self.stop_single_user(user)
|
yield self.stop_single_user(user, server_name)
|
||||||
status = 202 if user.stop_pending else 204
|
status = 202 if spawner._stop_pending else 204
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
|
|
||||||
class UserAdminAccessAPIHandler(APIHandler):
|
class UserAdminAccessAPIHandler(APIHandler):
|
||||||
@@ -288,7 +290,7 @@ default_handlers = [
|
|||||||
(r"/api/users", UserListAPIHandler),
|
(r"/api/users", UserListAPIHandler),
|
||||||
(r"/api/users/([^/]+)", UserAPIHandler),
|
(r"/api/users/([^/]+)", UserAPIHandler),
|
||||||
(r"/api/users/([^/]+)/server", UserServerAPIHandler),
|
(r"/api/users/([^/]+)/server", UserServerAPIHandler),
|
||||||
(r"/api/users/([^/]+)/servers", UserCreateNamedServerAPIHandler),
|
(r"/api/users/([^/]+)/servers/([^/]*)", UserCreateNamedServerAPIHandler),
|
||||||
(r"/api/users/([^/]+)/servers/([^/]+)", UserDeleteNamedServerAPIHandler),
|
(r"/api/users/([^/]+)/servers/([^/]*)", UserDeleteNamedServerAPIHandler),
|
||||||
(r"/api/users/([^/]+)/admin-access", UserAdminAccessAPIHandler),
|
(r"/api/users/([^/]+)/admin-access", UserAdminAccessAPIHandler),
|
||||||
]
|
]
|
||||||
|
@@ -319,12 +319,13 @@ class BaseHandler(RequestHandler):
|
|||||||
return self.settings.get('spawner_class', LocalProcessSpawner)
|
return self.settings.get('spawner_class', LocalProcessSpawner)
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def spawn_single_user(self, user, options=None):
|
def spawn_single_user(self, user, server_name='', options=None):
|
||||||
if user.spawn_pending:
|
if server_name in user.spawners and user.spawners[server_name]._spawn_pending:
|
||||||
raise RuntimeError("Spawn already pending for: %s" % user.name)
|
raise RuntimeError("Spawn already pending for: %s" % user.name)
|
||||||
tic = IOLoop.current().time()
|
tic = IOLoop.current().time()
|
||||||
|
|
||||||
f = user.spawn(options)
|
f = user.spawn(server_name, options)
|
||||||
|
spawner = user.spawners[server_name]
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def finish_user_spawn(f=None):
|
def finish_user_spawn(f=None):
|
||||||
@@ -340,14 +341,14 @@ class BaseHandler(RequestHandler):
|
|||||||
self.log.info("User %s server took %.3f seconds to start", user.name, toc-tic)
|
self.log.info("User %s server took %.3f seconds to start", user.name, toc-tic)
|
||||||
self.statsd.timing('spawner.success', (toc - tic) * 1000)
|
self.statsd.timing('spawner.success', (toc - tic) * 1000)
|
||||||
yield self.proxy.add_user(user)
|
yield self.proxy.add_user(user)
|
||||||
user.spawner.add_poll_callback(self.user_stopped, user)
|
spawner.add_poll_callback(self.user_stopped, user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f)
|
yield gen.with_timeout(timedelta(seconds=self.slow_spawn_timeout), f)
|
||||||
except gen.TimeoutError:
|
except gen.TimeoutError:
|
||||||
# waiting_for_response indicates server process has started,
|
# waiting_for_response indicates server process has started,
|
||||||
# but is yet to become responsive.
|
# but is yet to become responsive.
|
||||||
if not user.waiting_for_response:
|
if not spawner._waiting_for_response:
|
||||||
# still in Spawner.start, which is taking a long time
|
# still in Spawner.start, which is taking a long time
|
||||||
# we shouldn't poll while spawn is incomplete.
|
# we shouldn't poll while spawn is incomplete.
|
||||||
self.log.warning("User %s's server is slow to start (timeout=%s)",
|
self.log.warning("User %s's server is slow to start (timeout=%s)",
|
||||||
@@ -387,7 +388,7 @@ class BaseHandler(RequestHandler):
|
|||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def stop_single_user(self, user):
|
def stop_single_user(self, user):
|
||||||
if user.stop_pending:
|
if user.spawner._stop_pending:
|
||||||
raise RuntimeError("Stop already pending for: %s" % user.name)
|
raise RuntimeError("Stop already pending for: %s" % user.name)
|
||||||
tic = IOLoop.current().time()
|
tic = IOLoop.current().time()
|
||||||
yield self.proxy.delete_user(user)
|
yield self.proxy.delete_user(user)
|
||||||
@@ -408,7 +409,7 @@ class BaseHandler(RequestHandler):
|
|||||||
try:
|
try:
|
||||||
yield gen.with_timeout(timedelta(seconds=self.slow_stop_timeout), f)
|
yield gen.with_timeout(timedelta(seconds=self.slow_stop_timeout), f)
|
||||||
except gen.TimeoutError:
|
except gen.TimeoutError:
|
||||||
if user.stop_pending:
|
if user.spawner._stop_pending:
|
||||||
# hit timeout, but stop is still pending
|
# hit timeout, but stop is still pending
|
||||||
self.log.warning("User %s server is slow to stop", user.name)
|
self.log.warning("User %s server is slow to stop", user.name)
|
||||||
# schedule finish for when the server finishes stopping
|
# schedule finish for when the server finishes stopping
|
||||||
@@ -535,23 +536,23 @@ class UserSpawnHandler(BaseHandler):
|
|||||||
""", self.request.full_url(), self.proxy.public_url)
|
""", self.request.full_url(), self.proxy.public_url)
|
||||||
|
|
||||||
# logged in as correct user, spawn the server
|
# logged in as correct user, spawn the server
|
||||||
if current_user.spawner:
|
spawner = current_user.spawner
|
||||||
if current_user.spawn_pending:
|
if spawner._spawn_pending:
|
||||||
# spawn has started, but not finished
|
# spawn has started, but not finished
|
||||||
self.statsd.incr('redirects.user_spawn_pending', 1)
|
self.statsd.incr('redirects.user_spawn_pending', 1)
|
||||||
html = self.render_template("spawn_pending.html", user=current_user)
|
html = self.render_template("spawn_pending.html", user=current_user)
|
||||||
self.finish(html)
|
self.finish(html)
|
||||||
return
|
return
|
||||||
|
|
||||||
# spawn has supposedly finished, check on the status
|
# spawn has supposedly finished, check on the status
|
||||||
status = yield current_user.spawner.poll()
|
status = yield spawner.poll()
|
||||||
if status is not None:
|
if status is not None:
|
||||||
if current_user.spawner.options_form:
|
if spawner.options_form:
|
||||||
self.redirect(url_concat(url_path_join(self.hub.base_url, 'spawn'),
|
self.redirect(url_concat(url_path_join(self.hub.base_url, 'spawn'),
|
||||||
{'next': self.request.uri}))
|
{'next': self.request.uri}))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
yield self.spawn_single_user(current_user)
|
yield self.spawn_single_user(current_user)
|
||||||
# set login cookie anew
|
# set login cookie anew
|
||||||
self.set_login_cookie(current_user)
|
self.set_login_cookie(current_user)
|
||||||
without_prefix = self.request.uri[len(self.hub.base_url):]
|
without_prefix = self.request.uri[len(self.hub.base_url):]
|
||||||
|
@@ -140,7 +140,7 @@ class Proxy(LoggingConfigurable):
|
|||||||
user.name, user.proxy_path, user.server.host,
|
user.name, user.proxy_path, user.server.host,
|
||||||
)
|
)
|
||||||
|
|
||||||
if user.spawn_pending:
|
if user.spawner._spawn_pending:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"User %s's spawn is pending, shouldn't be added to the proxy yet!", user.name)
|
"User %s's spawn is pending, shouldn't be added to the proxy yet!", user.name)
|
||||||
|
|
||||||
|
@@ -46,6 +46,11 @@ class Spawner(LoggingConfigurable):
|
|||||||
instances of the subclass.
|
instances of the subclass.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# private attributes for tracking status
|
||||||
|
_spawn_pending = False
|
||||||
|
_stop_pending = False
|
||||||
|
_waiting_for_response = False
|
||||||
|
|
||||||
db = Any()
|
db = Any()
|
||||||
user = Any()
|
user = Any()
|
||||||
hub = Any()
|
hub = Any()
|
||||||
@@ -500,6 +505,8 @@ class Spawner(LoggingConfigurable):
|
|||||||
|
|
||||||
Doesn't expect shell expansion to happen.
|
Doesn't expect shell expansion to happen.
|
||||||
"""
|
"""
|
||||||
|
args = []
|
||||||
|
|
||||||
if self.ip:
|
if self.ip:
|
||||||
args.append('--ip="%s"' % self.ip)
|
args.append('--ip="%s"' % self.ip)
|
||||||
|
|
||||||
|
@@ -409,7 +409,7 @@ def test_spawn(app, io_loop):
|
|||||||
app_user = get_app_user(app, name)
|
app_user = get_app_user(app, name)
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
assert app_user.spawner.user_options == options
|
assert app_user.spawner.user_options == options
|
||||||
assert not app_user.spawn_pending
|
assert not app_user.spawner._spawn_pending
|
||||||
status = io_loop.run_sync(app_user.spawner.poll)
|
status = io_loop.run_sync(app_user.spawner.poll)
|
||||||
assert status is None
|
assert status is None
|
||||||
|
|
||||||
@@ -458,38 +458,38 @@ def test_slow_spawn(app, io_loop, no_patience, request):
|
|||||||
assert r.status_code == 202
|
assert r.status_code == 202
|
||||||
app_user = get_app_user(app, name)
|
app_user = get_app_user(app, name)
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
assert app_user.spawn_pending
|
assert app_user.spawner._spawn_pending
|
||||||
assert not app_user.stop_pending
|
assert not app_user.spawner._stop_pending
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def wait_spawn():
|
def wait_spawn():
|
||||||
while app_user.spawn_pending:
|
while app_user.spawner._spawn_pending:
|
||||||
yield gen.sleep(0.1)
|
yield gen.sleep(0.1)
|
||||||
|
|
||||||
io_loop.run_sync(wait_spawn)
|
io_loop.run_sync(wait_spawn)
|
||||||
assert not app_user.spawn_pending
|
assert not app_user.spawner._spawn_pending
|
||||||
status = io_loop.run_sync(app_user.spawner.poll)
|
status = io_loop.run_sync(app_user.spawner.poll)
|
||||||
assert status is None
|
assert status is None
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def wait_stop():
|
def wait_stop():
|
||||||
while app_user.stop_pending:
|
while app_user.pawner._stop_pending:
|
||||||
yield gen.sleep(0.1)
|
yield gen.sleep(0.1)
|
||||||
|
|
||||||
r = api_request(app, 'users', name, 'server', method='delete')
|
r = api_request(app, 'users', name, 'server', method='delete')
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
assert r.status_code == 202
|
assert r.status_code == 202
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
assert app_user.stop_pending
|
assert app_user.spawner._stop_pending
|
||||||
|
|
||||||
r = api_request(app, 'users', name, 'server', method='delete')
|
r = api_request(app, 'users', name, 'server', method='delete')
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
assert r.status_code == 202
|
assert r.status_code == 202
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
assert app_user.stop_pending
|
assert app_user.spawner._stop_pending
|
||||||
|
|
||||||
io_loop.run_sync(wait_stop)
|
io_loop.run_sync(wait_stop)
|
||||||
assert not app_user.stop_pending
|
assert not app_user.spawner._stop_pending
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
r = api_request(app, 'users', name, 'server', method='delete')
|
r = api_request(app, 'users', name, 'server', method='delete')
|
||||||
assert r.status_code == 400
|
assert r.status_code == 400
|
||||||
@@ -506,15 +506,15 @@ def test_never_spawn(app, io_loop, no_patience, request):
|
|||||||
r = api_request(app, 'users', name, 'server', method='post')
|
r = api_request(app, 'users', name, 'server', method='post')
|
||||||
app_user = get_app_user(app, name)
|
app_user = get_app_user(app, name)
|
||||||
assert app_user.spawner is not None
|
assert app_user.spawner is not None
|
||||||
assert app_user.spawn_pending
|
assert app_user.spawner._spawn_pending
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def wait_pending():
|
def wait_pending():
|
||||||
while app_user.spawn_pending:
|
while app_user.spawner._spawn_pending:
|
||||||
yield gen.sleep(0.1)
|
yield gen.sleep(0.1)
|
||||||
|
|
||||||
io_loop.run_sync(wait_pending)
|
io_loop.run_sync(wait_pending)
|
||||||
assert not app_user.spawn_pending
|
assert not app_user.spawner._spawn_pending
|
||||||
status = io_loop.run_sync(app_user.spawner.poll)
|
status = io_loop.run_sync(app_user.spawner.poll)
|
||||||
assert status is not None
|
assert status is not None
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from urllib.parse import quote, urlparse
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
@@ -96,13 +97,10 @@ class User(HasTraits):
|
|||||||
if self.orm_user:
|
if self.orm_user:
|
||||||
id = self.orm_user.id
|
id = self.orm_user.id
|
||||||
self.orm_user = change['new'].query(orm.User).filter(orm.User.id == id).first()
|
self.orm_user = change['new'].query(orm.User).filter(orm.User.id == id).first()
|
||||||
self.spawner.db = self.db
|
for spawner in self.spawners.values():
|
||||||
|
spawner.db = self.db
|
||||||
|
|
||||||
orm_user = None
|
orm_user = None
|
||||||
spawner = None
|
|
||||||
spawn_pending = False
|
|
||||||
stop_pending = False
|
|
||||||
waiting_for_response = False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def authenticator(self):
|
def authenticator(self):
|
||||||
@@ -112,7 +110,6 @@ class User(HasTraits):
|
|||||||
def spawner_class(self):
|
def spawner_class(self):
|
||||||
return self.settings.get('spawner_class', LocalProcessSpawner)
|
return self.settings.get('spawner_class', LocalProcessSpawner)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, orm_user, settings=None, **kwargs):
|
def __init__(self, orm_user, settings=None, **kwargs):
|
||||||
self.orm_user = orm_user
|
self.orm_user = orm_user
|
||||||
self.settings = settings or {}
|
self.settings = settings or {}
|
||||||
@@ -124,7 +121,11 @@ class User(HasTraits):
|
|||||||
self.base_url = url_path_join(
|
self.base_url = url_path_join(
|
||||||
self.settings.get('base_url', '/'), 'user', self.escaped_name)
|
self.settings.get('base_url', '/'), 'user', self.escaped_name)
|
||||||
|
|
||||||
self.spawner = self.spawner_class(
|
self.spawners = defaultdict(self._new_spawner)
|
||||||
|
|
||||||
|
def _new_spawner(self):
|
||||||
|
"""Create a new spawner"""
|
||||||
|
return self.spawner_class(
|
||||||
user=self,
|
user=self,
|
||||||
db=self.db,
|
db=self.db,
|
||||||
hub=self.settings.get('hub'),
|
hub=self.settings.get('hub'),
|
||||||
@@ -132,19 +133,14 @@ class User(HasTraits):
|
|||||||
config=self.settings.get('config'),
|
config=self.settings.get('config'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# singleton property, self.spawner maps onto spawner with empty server_name
|
||||||
@property
|
@property
|
||||||
def get_spawners(self):
|
def spawner(self):
|
||||||
return self._instances
|
return self.spawners['']
|
||||||
|
|
||||||
@property
|
@spawner.setter
|
||||||
def get_spawner(self, server_name):
|
def spawner(self, spawner):
|
||||||
try:
|
self.spawners[''] = spawner
|
||||||
return self._instances[server_name]
|
|
||||||
except KeyError as err:
|
|
||||||
self.log.warning("spawner for server named %s doesn't exist" % server_name)
|
|
||||||
|
|
||||||
def save_spawner(self, server_name):
|
|
||||||
self._instances[server_name] = self.spawner
|
|
||||||
|
|
||||||
# pass get/setattr to ORM user
|
# pass get/setattr to ORM user
|
||||||
|
|
||||||
@@ -163,12 +159,13 @@ class User(HasTraits):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return repr(self.orm_user)
|
return repr(self.orm_user)
|
||||||
|
|
||||||
@property # FIX-ME CHECK IF STILL NEEDED
|
@property
|
||||||
def running(self):
|
def running(self):
|
||||||
"""property for whether a user has a running server"""
|
"""property for whether a user has a running server"""
|
||||||
if self.spawn_pending or self.stop_pending:
|
spawner = self.spawner
|
||||||
|
if spawner._spawn_pending or spawner._stop_pending:
|
||||||
return False # server is not running if spawn or stop is still pending
|
return False # server is not running if spawn or stop is still pending
|
||||||
if self.server is None:
|
if spawner.server is None:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -220,7 +217,7 @@ class User(HasTraits):
|
|||||||
return self.base_url
|
return self.base_url
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def spawn(self, options=None):
|
def spawn(self, server_name='', options=None):
|
||||||
"""Start the user's spawner
|
"""Start the user's spawner
|
||||||
|
|
||||||
depending from the value of JupyterHub.allow_named_servers
|
depending from the value of JupyterHub.allow_named_servers
|
||||||
@@ -234,15 +231,10 @@ class User(HasTraits):
|
|||||||
url of the server will be /user/:name/:server_name
|
url of the server will be /user/:name/:server_name
|
||||||
"""
|
"""
|
||||||
db = self.db
|
db = self.db
|
||||||
if self.allow_named_servers:
|
if self.allow_named_servers and not server_name:
|
||||||
if options is not None and 'server_name' in options:
|
server_name = default_server_name(self)
|
||||||
server_name = options['server_name']
|
|
||||||
else:
|
base_url = url_path_join(self.base_url, server_name)
|
||||||
server_name = default_server_name(self)
|
|
||||||
base_url = url_path_join(self.base_url, server_name)
|
|
||||||
else:
|
|
||||||
server_name = ''
|
|
||||||
base_url = self.base_url
|
|
||||||
|
|
||||||
orm_server = orm.Server(
|
orm_server = orm.Server(
|
||||||
name=server_name,
|
name=server_name,
|
||||||
@@ -255,14 +247,10 @@ class User(HasTraits):
|
|||||||
|
|
||||||
server = Server(orm_server=orm_server)
|
server = Server(orm_server=orm_server)
|
||||||
|
|
||||||
spawner = self.spawner
|
spawner = self.spawners[server_name]
|
||||||
|
|
||||||
# Save spawner's instance inside self._instances
|
|
||||||
self.save_spawner(server_name)
|
|
||||||
|
|
||||||
# Passing server, server_name and options to the spawner
|
# Passing server, server_name and options to the spawner
|
||||||
spawner.server = server
|
spawner.server = server
|
||||||
spawner.server_name = server_name
|
|
||||||
spawner.user_options = options or {}
|
spawner.user_options = options or {}
|
||||||
# we are starting a new server, make sure it doesn't restore state
|
# we are starting a new server, make sure it doesn't restore state
|
||||||
spawner.clear_state()
|
spawner.clear_state()
|
||||||
@@ -294,7 +282,7 @@ class User(HasTraits):
|
|||||||
if (authenticator):
|
if (authenticator):
|
||||||
yield gen.maybe_future(authenticator.pre_spawn_start(self, spawner))
|
yield gen.maybe_future(authenticator.pre_spawn_start(self, spawner))
|
||||||
|
|
||||||
self.spawn_pending = True
|
spawner._spawn_pending = True
|
||||||
# wait for spawner.start to return
|
# wait for spawner.start to return
|
||||||
try:
|
try:
|
||||||
f = spawner.start()
|
f = spawner.start()
|
||||||
@@ -336,10 +324,10 @@ class User(HasTraits):
|
|||||||
spawner.start_polling()
|
spawner.start_polling()
|
||||||
|
|
||||||
# store state
|
# store state
|
||||||
self.state = spawner.get_state()
|
self.state[server_name] = spawner.get_state()
|
||||||
self.last_activity = datetime.utcnow()
|
self.last_activity = datetime.utcnow()
|
||||||
db.commit()
|
db.commit()
|
||||||
self.waiting_for_response = True
|
spawner._waiting_for_response = True
|
||||||
try:
|
try:
|
||||||
yield server.wait_up(http=True, timeout=spawner.http_timeout)
|
yield server.wait_up(http=True, timeout=spawner.http_timeout)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -367,32 +355,30 @@ class User(HasTraits):
|
|||||||
# raise original TimeoutError
|
# raise original TimeoutError
|
||||||
raise e
|
raise e
|
||||||
finally:
|
finally:
|
||||||
self.waiting_for_response = False
|
spawner._waiting_for_response = False
|
||||||
self.spawn_pending = False
|
spawner._spawn_pending = False
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def stop(self):
|
def stop(self, server_name=''):
|
||||||
"""Stop the user's spawner
|
"""Stop the user's spawner
|
||||||
|
|
||||||
and cleanup after it.
|
and cleanup after it.
|
||||||
"""
|
"""
|
||||||
self.spawn_pending = False
|
spawner = self.spawners[server_name]
|
||||||
spawner = self.spawner
|
spawner._spawn_pending = False
|
||||||
self.spawner.stop_polling()
|
spawner.stop_polling()
|
||||||
self.stop_pending = True
|
spawner._stop_pending = True
|
||||||
try:
|
try:
|
||||||
api_token = self.spawner.api_token
|
api_token = spawner.api_token
|
||||||
status = yield spawner.poll()
|
status = yield spawner.poll()
|
||||||
if status is None:
|
if status is None:
|
||||||
yield self.spawner.stop()
|
yield spawner.stop()
|
||||||
spawner.clear_state()
|
spawner.clear_state()
|
||||||
self.state = spawner.get_state()
|
self.state = spawner.get_state()
|
||||||
self.last_activity = datetime.utcnow()
|
self.last_activity = datetime.utcnow()
|
||||||
# Cleanup defunct servers: delete entry and API token for each server
|
# remove server entry from db
|
||||||
for server in self.servers:
|
self.db.delete(spawner.server.orm_server)
|
||||||
# remove server entry from db
|
|
||||||
self.db.delete(server)
|
|
||||||
if not spawner.will_resume:
|
if not spawner.will_resume:
|
||||||
# find and remove the API token if the spawner isn't
|
# find and remove the API token if the spawner isn't
|
||||||
# going to re-use it next time
|
# going to re-use it next time
|
||||||
@@ -401,7 +387,7 @@ class User(HasTraits):
|
|||||||
self.db.delete(orm_token)
|
self.db.delete(orm_token)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
finally:
|
finally:
|
||||||
self.stop_pending = False
|
spawner._stop_pending = False
|
||||||
# trigger post-spawner hook on authenticator
|
# trigger post-spawner hook on authenticator
|
||||||
auth = spawner.authenticator
|
auth = spawner.authenticator
|
||||||
if auth:
|
if auth:
|
||||||
|
Reference in New Issue
Block a user