return (ip, port) from Spawner.start

removes the need for Spawners to set db state themselves in most cases

Should be backward-compatible with warnings.
This commit is contained in:
Min RK
2016-07-22 14:26:15 +02:00
parent 22ff5f3d91
commit 3159b61ae7
4 changed files with 51 additions and 16 deletions

View File

@@ -46,6 +46,9 @@ class Spawner(LoggingConfigurable):
ip = Unicode('127.0.0.1',
help="The IP address (or hostname) the single-user server should listen on"
).tag(config=True)
port = Integer(0,
help="The port for single-user servers to listen on. New in version 0.7."
)
start_timeout = Integer(60,
help="""Timeout (in seconds) before giving up on the spawner.
@@ -245,7 +248,6 @@ class Spawner(LoggingConfigurable):
"""Return the arguments to be passed after self.cmd"""
args = [
'--user=%s' % self.user.name,
'--port=%i' % self.user.server.port,
'--cookie-name=%s' % self.user.server.cookie_name,
'--base-url=%s' % self.user.server.base_url,
'--hub-host=%s' % self.hub.host,
@@ -254,6 +256,13 @@ class Spawner(LoggingConfigurable):
]
if self.ip:
args.append('--ip=%s' % self.ip)
if self.port:
args.append('--port=%i' % self.port)
elif self.user.server.port:
self.log.warning("Setting port from user.server is deprecated as of JupyterHub 0.7.")
args.append('--port=%i' % self.user.server.port)
if self.notebook_dir:
self.notebook_dir = self.notebook_dir.replace("%U",self.user.name)
args.append('--notebook-dir=%s' % self.notebook_dir)
@@ -270,7 +279,15 @@ class Spawner(LoggingConfigurable):
@gen.coroutine
def start(self):
"""Start the single-user process"""
"""Start the single-user server
Returns:
(ip, port): the ip, port where the Hub can connect to the server.
.. versionchanged:: 0.7
Return ip, port instead of setting on self.user.server directly.
"""
raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")
@gen.coroutine
@@ -456,9 +473,7 @@ class LocalProcessSpawner(Spawner):
@gen.coroutine
def start(self):
"""Start the process"""
if self.ip:
self.user.server.ip = self.ip
self.user.server.port = random_port()
self.port = random_port()
cmd = []
env = self.get_env()
@@ -480,6 +495,7 @@ class LocalProcessSpawner(Spawner):
raise
self.pid = self.proc.pid
return (self.ip or '127.0.0.1', self.port)
@gen.coroutine
def poll(self):

View File

@@ -62,8 +62,9 @@ class SlowSpawner(MockSpawner):
@gen.coroutine
def start(self):
yield super().start()
(ip, port) = yield super().start()
yield gen.sleep(2)
return ip, port
@gen.coroutine
def stop(self):

View File

@@ -50,8 +50,14 @@ def new_spawner(db, **kwargs):
def test_spawner(db, io_loop):
spawner = new_spawner(db)
io_loop.run_sync(spawner.start)
assert spawner.user.server.ip == '127.0.0.1'
ip, port = io_loop.run_sync(spawner.start)
assert ip == '127.0.0.1'
assert isinstance(port, int)
assert port > 0
spawner.user.server.ip = ip
spawner.user.server.port = port
db.commit()
# wait for the process to get to the while True: loop
time.sleep(1)
@@ -65,8 +71,13 @@ def test_spawner(db, io_loop):
def test_single_user_spawner(db, io_loop):
spawner = new_spawner(db, cmd=['jupyterhub-singleuser'])
spawner.api_token = 'secret'
io_loop.run_sync(spawner.start)
assert spawner.user.server.ip == '127.0.0.1'
ip, port = io_loop.run_sync(spawner.start)
assert ip == '127.0.0.1'
assert isinstance(port, int)
assert port > 0
spawner.user.server.ip = ip
spawner.user.server.port = port
db.commit()
# wait for http server to come up,
# checking for early termination every 1s
def wait():

View File

@@ -225,7 +225,14 @@ class User(HasTraits):
f = spawner.start()
# commit any changes in spawner.start (always commit db changes before yield)
db.commit()
yield gen.with_timeout(timedelta(seconds=spawner.start_timeout), f)
ip_port = yield gen.with_timeout(timedelta(seconds=spawner.start_timeout), f)
if ip_port:
# get ip, port info from return value of start()
self.server.ip, self.server.port = ip_port
else:
# prior to 0.7, spawners had to store this info in user.server themselves.
# Handle < 0.7 behavior with a warning, assuming info was stored in db by the Spawner.
self.log.warning("DEPRECATION: Spawner.start should return (ip, port) in JupyterHub >= 0.7")
except Exception as e:
if isinstance(e, gen.TimeoutError):
self.log.warning("{user}'s server failed to start in {s} seconds, giving up".format(