mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 13:33:00 +00:00
add configurable authentication
and coroutine auth and spawning
This commit is contained in:
@@ -11,6 +11,7 @@ import time
|
||||
from subprocess import Popen
|
||||
|
||||
from tornado import gen
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
from IPython.config import LoggingConfigurable
|
||||
from IPython.utils.traitlets import (
|
||||
@@ -18,7 +19,7 @@ from IPython.utils.traitlets import (
|
||||
)
|
||||
|
||||
|
||||
from .utils import random_port, wait_for_server
|
||||
from .utils import random_port
|
||||
|
||||
|
||||
class Spawner(LoggingConfigurable):
|
||||
@@ -103,15 +104,18 @@ class Spawner(LoggingConfigurable):
|
||||
'--hub-prefix=%s' % self.hub.server.base_url,
|
||||
'--hub-api-url=%s' % self.hub.api_url,
|
||||
]
|
||||
|
||||
|
||||
@gen.coroutine
|
||||
def start(self):
|
||||
raise NotImplementedError("Override in subclass")
|
||||
raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")
|
||||
|
||||
@gen.coroutine
|
||||
def stop(self):
|
||||
raise NotImplementedError("Override in subclass")
|
||||
raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")
|
||||
|
||||
@gen.coroutine
|
||||
def poll(self):
|
||||
raise NotImplementedError("Override in subclass")
|
||||
raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")
|
||||
|
||||
|
||||
class LocalProcessSpawner(Spawner):
|
||||
@@ -125,6 +129,7 @@ class LocalProcessSpawner(Spawner):
|
||||
def get_state(self):
|
||||
return dict(pid=self.pid)
|
||||
|
||||
@gen.coroutine
|
||||
def start(self):
|
||||
self.user.server.port = random_port()
|
||||
cmd = self.cmd + self.get_args()
|
||||
@@ -136,10 +141,11 @@ class LocalProcessSpawner(Spawner):
|
||||
)
|
||||
self.pid = self.proc.pid
|
||||
|
||||
@gen.coroutine
|
||||
def poll(self):
|
||||
# if we started the process, poll with Popen
|
||||
if self.proc is not None:
|
||||
return self.proc.poll()
|
||||
raise gen.Return(self.proc.poll())
|
||||
|
||||
# if we resumed from stored state,
|
||||
# we don't have the Popen handle anymore
|
||||
@@ -150,38 +156,62 @@ class LocalProcessSpawner(Spawner):
|
||||
except OSError as e:
|
||||
if e.errno == errno.ESRCH:
|
||||
# no such process, return exitcode == 0, since we don't know the exit status
|
||||
return 0
|
||||
raise gen.Return(0)
|
||||
else:
|
||||
# None indicates the process is running
|
||||
return None
|
||||
raise gen.Return(None)
|
||||
|
||||
@gen.coroutine
|
||||
def _wait_for_death(self, timeout=10):
|
||||
"""wait for the process to die, up to timeout seconds"""
|
||||
for i in range(int(timeout * 10)):
|
||||
if self.poll() is not None:
|
||||
status = yield self.poll()
|
||||
if status is not None:
|
||||
break
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
yield gen.Task(IOLoop.instance().add_timeout, time.time() + 0.1)
|
||||
|
||||
@gen.coroutine
|
||||
def stop(self, now=False):
|
||||
"""stop the subprocess"""
|
||||
"""stop the subprocess
|
||||
|
||||
if `now`, skip waiting for clean shutdown
|
||||
"""
|
||||
if not now:
|
||||
# double-sigint to request clean shutdown
|
||||
os.kill(self.pid, signal.SIGINT)
|
||||
os.kill(self.pid, signal.SIGINT)
|
||||
self._wait_for_death(10)
|
||||
# SIGINT to request clean shutdown
|
||||
self.log.debug("Interrupting %i", self.pid)
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGINT)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ESRCH:
|
||||
return
|
||||
|
||||
yield self._wait_for_death(10)
|
||||
|
||||
# clean shutdown failed, use TERM
|
||||
if self.poll() is None:
|
||||
os.kill(self.pid, signal.SIGTERM)
|
||||
self._wait_for_death(5)
|
||||
status = yield self.poll()
|
||||
if status is None:
|
||||
self.log.debug("Terminating %i", self.pid)
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGTERM)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ESRCH:
|
||||
return
|
||||
yield self._wait_for_death(5)
|
||||
|
||||
# TERM failed, use KILL
|
||||
if self.poll() is None:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
self._wait_for_death(5)
|
||||
status = yield self.poll()
|
||||
if status is None:
|
||||
self.log.debug("Killing %i", self.pid)
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ESRCH:
|
||||
return
|
||||
yield self._wait_for_death(5)
|
||||
|
||||
if self.poll() is None:
|
||||
status = yield self.poll()
|
||||
if status is None:
|
||||
# it all failed, zombie process
|
||||
self.log.warn("Process %i never died", self.pid)
|
||||
|
||||
|
Reference in New Issue
Block a user