split multiuser files

This commit is contained in:
MinRK
2014-07-09 09:41:42 -05:00
parent d3c95c7da5
commit 5e07fd1ce0
6 changed files with 510 additions and 413 deletions

180
multiuser/user.py Normal file
View File

@@ -0,0 +1,180 @@
"""Classes for managing users."""
import json
import os
import signal
import sys
import time
import uuid
from subprocess import Popen
import requests
from tornado.log import app_log
from IPython.utils.traitlets import Any, Unicode, Integer, Dict
from IPython.config import LoggingConfigurable
from .utils import random_port, wait_for_server
class UserSession(LoggingConfigurable):
env_prefix = Unicode('IPY_')
process = Any()
port = Integer()
user = Unicode()
cookie_secret = Unicode()
cookie_name = Unicode()
def _cookie_name_default(self):
return 'ipy-multiuser-%s' % self.user
multiuser_prefix = Unicode()
multiuser_api_url = Unicode()
url_prefix = Unicode()
def _url_prefix_default(self):
return '/user/%s/' % self.user
api_token = Unicode()
def _api_token_default(self):
return str(uuid.uuid4())
cookie_token = Unicode()
def _cookie_token_default(self):
return str(uuid.uuid4())
def _env_key(self, d, key, value):
d['%s%s' % (self.env_prefix, key)] = value
env = Dict()
def _env_default(self):
env = os.environ.copy()
self._env_key(env, 'COOKIE_SECRET', self.cookie_secret)
self._env_key(env, 'API_TOKEN', self.api_token)
return env
@property
def auth_data(self):
return dict(
user=self.user,
)
def start(self):
assert self.process is None or self.process.poll() is not None
cmd = [sys.executable, '-m', 'multiuser.singleuser',
'--user=%s' % self.user, '--port=%i' % self.port,
'--cookie-name=%s' % self.cookie_name,
'--multiuser-prefix=%s' % self.multiuser_prefix,
'--multiuser-api-url=%s' % self.multiuser_api_url,
'--base-url=%s' % self.url_prefix,
]
app_log.info("Spawning: %s" % cmd)
self.process = Popen(cmd, env=self.env,
# don't forward signals:
preexec_fn=os.setpgrp,
)
def running(self):
if self.process is None:
return False
if self.process.poll() is not None:
self.process = None
return False
return True
def request_stop(self):
if self.running():
self.process.send_signal(signal.SIGINT)
time.sleep(0.1)
if self.running():
self.process.send_signal(signal.SIGINT)
def stop(self):
for i in range(100):
if self.running():
time.sleep(0.1)
else:
break
if self.running():
self.process.terminate()
class UserManager(LoggingConfigurable):
users = Dict()
routes_t = Unicode('http://{ip}:{port}/api/routes{uri}')
single_user_t = Unicode('http://{ip}:{port}')
single_user_ip = Unicode('localhost')
proxy_ip = Unicode('localhost')
proxy_port = Integer(8001)
proxy_auth_token = Unicode()
def _proxy_auth_token_default(self):
return str(uuid.uuid4())
def get_session(self, user, **kwargs):
if user not in self.users:
kwargs['user'] = user
self.users[user] = UserSession(**kwargs)
return self.users[user]
def spawn(self, user):
session = self.get_session(user)
if session.running():
app_log.warn("User session %s already running", user)
return
session.port = port = random_port()
session.start()
r = requests.post(
self.routes_t.format(
ip=self.proxy_ip,
port=self.proxy_port,
uri=session.url_prefix,
),
data=json.dumps(dict(
target=self.single_user_t.format(
ip=self.single_user_ip,
port=port
),
user=user,
)),
headers={'Authorization': "token %s" % self.proxy_auth_token},
)
wait_for_server(self.single_user_ip, port)
r.raise_for_status()
def user_for_api_token(self, token):
"""Get the user session object for a given API token"""
for session in self.users.values():
if session.api_token == token:
return session
def user_for_cookie_token(self, token):
"""Get the user session object for a given cookie token"""
for session in self.users.values():
if session.cookie_token == token:
return session
def shutdown(self, user):
assert user in self.users
session = self.users.pop(user)
session.stop()
r = requests.delete(self.routes_url,
data=json.dumps(user=user, port=session.port),
)
r.raise_for_status()
def cleanup(self):
sessions = list(self.users.values())
self.users = {}
for session in sessions:
self.log.info("Cleaning up %s's server" % session.user)
session.request_stop()
for i in range(100):
if any([ session.running() for session in sessions ]):
time.sleep(0.1)
else:
break
for session in sessions:
session.stop()