mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 13:33:00 +00:00
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:
@@ -46,6 +46,9 @@ class Spawner(LoggingConfigurable):
|
|||||||
ip = Unicode('127.0.0.1',
|
ip = Unicode('127.0.0.1',
|
||||||
help="The IP address (or hostname) the single-user server should listen on"
|
help="The IP address (or hostname) the single-user server should listen on"
|
||||||
).tag(config=True)
|
).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,
|
start_timeout = Integer(60,
|
||||||
help="""Timeout (in seconds) before giving up on the spawner.
|
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"""
|
"""Return the arguments to be passed after self.cmd"""
|
||||||
args = [
|
args = [
|
||||||
'--user=%s' % self.user.name,
|
'--user=%s' % self.user.name,
|
||||||
'--port=%i' % self.user.server.port,
|
|
||||||
'--cookie-name=%s' % self.user.server.cookie_name,
|
'--cookie-name=%s' % self.user.server.cookie_name,
|
||||||
'--base-url=%s' % self.user.server.base_url,
|
'--base-url=%s' % self.user.server.base_url,
|
||||||
'--hub-host=%s' % self.hub.host,
|
'--hub-host=%s' % self.hub.host,
|
||||||
@@ -254,6 +256,13 @@ class Spawner(LoggingConfigurable):
|
|||||||
]
|
]
|
||||||
if self.ip:
|
if self.ip:
|
||||||
args.append('--ip=%s' % 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:
|
if self.notebook_dir:
|
||||||
self.notebook_dir = self.notebook_dir.replace("%U",self.user.name)
|
self.notebook_dir = self.notebook_dir.replace("%U",self.user.name)
|
||||||
args.append('--notebook-dir=%s' % self.notebook_dir)
|
args.append('--notebook-dir=%s' % self.notebook_dir)
|
||||||
@@ -270,7 +279,15 @@ class Spawner(LoggingConfigurable):
|
|||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def start(self):
|
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.")
|
raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
@@ -456,15 +473,13 @@ class LocalProcessSpawner(Spawner):
|
|||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the process"""
|
"""Start the process"""
|
||||||
if self.ip:
|
self.port = random_port()
|
||||||
self.user.server.ip = self.ip
|
|
||||||
self.user.server.port = random_port()
|
|
||||||
cmd = []
|
cmd = []
|
||||||
env = self.get_env()
|
env = self.get_env()
|
||||||
|
|
||||||
cmd.extend(self.cmd)
|
cmd.extend(self.cmd)
|
||||||
cmd.extend(self.get_args())
|
cmd.extend(self.get_args())
|
||||||
|
|
||||||
self.log.info("Spawning %s", ' '.join(pipes.quote(s) for s in cmd))
|
self.log.info("Spawning %s", ' '.join(pipes.quote(s) for s in cmd))
|
||||||
try:
|
try:
|
||||||
self.proc = Popen(cmd, env=env,
|
self.proc = Popen(cmd, env=env,
|
||||||
@@ -478,9 +493,10 @@ class LocalProcessSpawner(Spawner):
|
|||||||
script, self.user.name,
|
script, self.user.name,
|
||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
self.pid = self.proc.pid
|
self.pid = self.proc.pid
|
||||||
|
return (self.ip or '127.0.0.1', self.port)
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def poll(self):
|
def poll(self):
|
||||||
"""Poll the process"""
|
"""Poll the process"""
|
||||||
|
@@ -62,8 +62,9 @@ class SlowSpawner(MockSpawner):
|
|||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def start(self):
|
def start(self):
|
||||||
yield super().start()
|
(ip, port) = yield super().start()
|
||||||
yield gen.sleep(2)
|
yield gen.sleep(2)
|
||||||
|
return ip, port
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
@@ -50,12 +50,18 @@ def new_spawner(db, **kwargs):
|
|||||||
|
|
||||||
def test_spawner(db, io_loop):
|
def test_spawner(db, io_loop):
|
||||||
spawner = new_spawner(db)
|
spawner = new_spawner(db)
|
||||||
io_loop.run_sync(spawner.start)
|
ip, port = io_loop.run_sync(spawner.start)
|
||||||
assert spawner.user.server.ip == '127.0.0.1'
|
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
|
# wait for the process to get to the while True: loop
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
status = io_loop.run_sync(spawner.poll)
|
status = io_loop.run_sync(spawner.poll)
|
||||||
assert status is None
|
assert status is None
|
||||||
io_loop.run_sync(spawner.stop)
|
io_loop.run_sync(spawner.stop)
|
||||||
@@ -65,8 +71,13 @@ def test_spawner(db, io_loop):
|
|||||||
def test_single_user_spawner(db, io_loop):
|
def test_single_user_spawner(db, io_loop):
|
||||||
spawner = new_spawner(db, cmd=['jupyterhub-singleuser'])
|
spawner = new_spawner(db, cmd=['jupyterhub-singleuser'])
|
||||||
spawner.api_token = 'secret'
|
spawner.api_token = 'secret'
|
||||||
io_loop.run_sync(spawner.start)
|
ip, port = io_loop.run_sync(spawner.start)
|
||||||
assert spawner.user.server.ip == '127.0.0.1'
|
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,
|
# wait for http server to come up,
|
||||||
# checking for early termination every 1s
|
# checking for early termination every 1s
|
||||||
def wait():
|
def wait():
|
||||||
|
@@ -225,7 +225,14 @@ class User(HasTraits):
|
|||||||
f = spawner.start()
|
f = spawner.start()
|
||||||
# commit any changes in spawner.start (always commit db changes before yield)
|
# commit any changes in spawner.start (always commit db changes before yield)
|
||||||
db.commit()
|
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:
|
except Exception as e:
|
||||||
if isinstance(e, gen.TimeoutError):
|
if isinstance(e, gen.TimeoutError):
|
||||||
self.log.warning("{user}'s server failed to start in {s} seconds, giving up".format(
|
self.log.warning("{user}'s server failed to start in {s} seconds, giving up".format(
|
||||||
|
Reference in New Issue
Block a user