add configurable authentication

and coroutine auth and spawning
This commit is contained in:
MinRK
2014-08-20 11:05:10 -07:00
parent 26fe357f11
commit a156d09d11
8 changed files with 243 additions and 72 deletions

View File

@@ -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)