mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-13 13:03:01 +00:00
various cleanup to get most tests passing (yay!)
This commit is contained in:
3
.flake8
3
.flake8
@@ -4,9 +4,10 @@
|
|||||||
# W: style warnings
|
# W: style warnings
|
||||||
# C: complexity
|
# C: complexity
|
||||||
# F401: module imported but unused
|
# F401: module imported but unused
|
||||||
|
# F403: import *
|
||||||
# F811: redefinition of unused `name` from line `N`
|
# F811: redefinition of unused `name` from line `N`
|
||||||
# F841: local variable assigned but never used
|
# F841: local variable assigned but never used
|
||||||
ignore = E, C, W, F401, F811, F841
|
ignore = E, C, W, F401, F403, F811, F841
|
||||||
|
|
||||||
exclude =
|
exclude =
|
||||||
.cache,
|
.cache,
|
||||||
|
@@ -224,7 +224,7 @@ class UserCreateNamedServerAPIHandler(APIHandler):
|
|||||||
def post(self, name):
|
def post(self, name):
|
||||||
user = self.find_user(name)
|
user = self.find_user(name)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise HTTPError(404, "No such user %r" % name)
|
raise web.HTTPError(404, "No such user %r" % name)
|
||||||
if user.running:
|
if user.running:
|
||||||
# include notify, so that a server that died is noticed immediately
|
# include notify, so that a server that died is noticed immediately
|
||||||
state = yield user.spawner.poll_and_notify()
|
state = yield user.spawner.poll_and_notify()
|
||||||
|
@@ -1107,10 +1107,11 @@ class JupyterHub(Application):
|
|||||||
def init_proxy(self):
|
def init_proxy(self):
|
||||||
"""Load the Proxy config"""
|
"""Load the Proxy config"""
|
||||||
# FIXME: handle deprecated config here
|
# FIXME: handle deprecated config here
|
||||||
public_url = 'http{s}://{ip}:{port}'.format(
|
public_url = 'http{s}://{ip}:{port}{base_url}'.format(
|
||||||
s='s' if self.ssl_cert else '',
|
s='s' if self.ssl_cert else '',
|
||||||
ip=self.ip,
|
ip=self.ip,
|
||||||
port=self.port,
|
port=self.port,
|
||||||
|
base_url=self.base_url,
|
||||||
)
|
)
|
||||||
self.proxy = self.proxy_class(
|
self.proxy = self.proxy_class(
|
||||||
db=self.db,
|
db=self.db,
|
||||||
@@ -1309,21 +1310,22 @@ class JupyterHub(Application):
|
|||||||
users_count = 0
|
users_count = 0
|
||||||
active_users_count = 0
|
active_users_count = 0
|
||||||
for prefix, route in routes.items():
|
for prefix, route in routes.items():
|
||||||
if 'user' not in route['data']:
|
route_data = route['data']
|
||||||
|
if 'user' not in route_data:
|
||||||
# not a user route, ignore it
|
# not a user route, ignore it
|
||||||
continue
|
continue
|
||||||
users_count += 1
|
users_count += 1
|
||||||
if 'last_activity' not in route['data']:
|
if 'last_activity' not in route_data:
|
||||||
# no last activity data (possibly proxy other than CHP)
|
# no last activity data (possibly proxy other than CHP)
|
||||||
continue
|
continue
|
||||||
user = orm.User.find(self.db, route['user'])
|
user = orm.User.find(self.db, route_data['user'])
|
||||||
if user is None:
|
if user is None:
|
||||||
self.log.warning("Found no user for route: %s", route)
|
self.log.warning("Found no user for route: %s", route)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
dt = datetime.strptime(route['last_activity'], ISO8601_ms)
|
dt = datetime.strptime(route_data['last_activity'], ISO8601_ms)
|
||||||
except Exception:
|
except Exception:
|
||||||
dt = datetime.strptime(route['last_activity'], ISO8601_s)
|
dt = datetime.strptime(route_data['last_activity'], ISO8601_s)
|
||||||
user.last_activity = max(user.last_activity, dt)
|
user.last_activity = max(user.last_activity, dt)
|
||||||
# FIXME: Make this configurable duration. 30 minutes for now!
|
# FIXME: Make this configurable duration. 30 minutes for now!
|
||||||
if (datetime.now() - user.last_activity).total_seconds() < 30 * 60:
|
if (datetime.now() - user.last_activity).total_seconds() < 30 * 60:
|
||||||
|
@@ -89,5 +89,4 @@ def _alembic(*args):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
|
||||||
_alembic(*sys.argv[1:])
|
_alembic(*sys.argv[1:])
|
||||||
|
@@ -17,8 +17,9 @@ from tornado.web import RequestHandler
|
|||||||
from tornado import gen, web
|
from tornado import gen, web
|
||||||
|
|
||||||
from .. import orm
|
from .. import orm
|
||||||
from ..user import User
|
from ..objects import Server
|
||||||
from ..spawner import LocalProcessSpawner
|
from ..spawner import LocalProcessSpawner
|
||||||
|
from ..user import User
|
||||||
from ..utils import url_path_join
|
from ..utils import url_path_join
|
||||||
|
|
||||||
# pattern for the authentication token header
|
# pattern for the authentication token header
|
||||||
|
@@ -43,7 +43,7 @@ class Server(HasTraits):
|
|||||||
port = 443
|
port = 443
|
||||||
else:
|
else:
|
||||||
port = 80
|
port = 80
|
||||||
return cls(proto=proto, ip=ip, port=port)
|
return cls(proto=proto, ip=ip, port=port, base_url=urlinfo.path)
|
||||||
|
|
||||||
@default('port')
|
@default('port')
|
||||||
def _default_port(self):
|
def _default_port(self):
|
||||||
|
@@ -148,16 +148,6 @@ class User(Base):
|
|||||||
# group mapping
|
# group mapping
|
||||||
groups = relationship('Group', secondary='user_group_map', back_populates='users')
|
groups = relationship('Group', secondary='user_group_map', back_populates='users')
|
||||||
|
|
||||||
@property
|
|
||||||
def server(self):
|
|
||||||
"""Returns the first element of servers.
|
|
||||||
Returns None if the list is empty.
|
|
||||||
"""
|
|
||||||
if len(self.servers) == 0:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return self.servers[0]
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.server:
|
if self.server:
|
||||||
return "<{cls}({name}@{ip}:{port})>".format(
|
return "<{cls}({name}@{ip}:{port})>".format(
|
||||||
|
@@ -365,11 +365,11 @@ class ConfigurableHTTPProxy(Proxy):
|
|||||||
def api_request(self, path, method='GET', body=None, client=None):
|
def api_request(self, path, method='GET', body=None, client=None):
|
||||||
"""Make an authenticated API request of the proxy."""
|
"""Make an authenticated API request of the proxy."""
|
||||||
client = client or AsyncHTTPClient()
|
client = client or AsyncHTTPClient()
|
||||||
url = url_path_join(self.api_url, path)
|
url = url_path_join(self.api_url, 'api/routes', path)
|
||||||
|
|
||||||
if isinstance(body, dict):
|
if isinstance(body, dict):
|
||||||
body = json.dumps(body)
|
body = json.dumps(body)
|
||||||
self.log.debug("Fetching %s %s", method, url)
|
self.log.debug("Proxy: Fetching %s %s", method, url)
|
||||||
req = HTTPRequest(url,
|
req = HTTPRequest(url,
|
||||||
method=method,
|
method=method,
|
||||||
headers={'Authorization': 'token {}'.format(
|
headers={'Authorization': 'token {}'.format(
|
||||||
|
@@ -52,6 +52,7 @@ from traitlets import (
|
|||||||
from traitlets.config import LoggingConfigurable
|
from traitlets.config import LoggingConfigurable
|
||||||
|
|
||||||
from .. import orm
|
from .. import orm
|
||||||
|
from ..objects import Server
|
||||||
from ..traitlets import Command
|
from ..traitlets import Command
|
||||||
from ..spawner import LocalProcessSpawner, set_user_setuid
|
from ..spawner import LocalProcessSpawner, set_user_setuid
|
||||||
from ..utils import url_path_join
|
from ..utils import url_path_join
|
||||||
@@ -60,7 +61,7 @@ class _MockUser(HasTraits):
|
|||||||
name = Unicode()
|
name = Unicode()
|
||||||
server = Instance(orm.Server, allow_none=True)
|
server = Instance(orm.Server, allow_none=True)
|
||||||
state = Dict()
|
state = Dict()
|
||||||
service = Instance(__module__ + '.Service')
|
service = Instance(__name__ + '.Service')
|
||||||
host = Unicode()
|
host = Unicode()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -221,7 +222,10 @@ class Service(LoggingConfigurable):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def server(self):
|
def server(self):
|
||||||
return self.orm.server
|
if self.orm.server:
|
||||||
|
return Server(orm_server=self.orm.server)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prefix(self):
|
def prefix(self):
|
||||||
|
@@ -4,7 +4,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@@ -18,6 +17,7 @@ from traitlets import default
|
|||||||
from ..app import JupyterHub
|
from ..app import JupyterHub
|
||||||
from ..auth import PAMAuthenticator
|
from ..auth import PAMAuthenticator
|
||||||
from .. import orm
|
from .. import orm
|
||||||
|
from ..objects import Server
|
||||||
from ..spawner import LocalProcessSpawner
|
from ..spawner import LocalProcessSpawner
|
||||||
from ..singleuser import SingleUserNotebookApp
|
from ..singleuser import SingleUserNotebookApp
|
||||||
from ..utils import random_port, url_path_join
|
from ..utils import random_port, url_path_join
|
||||||
@@ -207,7 +207,7 @@ def public_host(app):
|
|||||||
if app.subdomain_host:
|
if app.subdomain_host:
|
||||||
return app.subdomain_host
|
return app.subdomain_host
|
||||||
else:
|
else:
|
||||||
return urlparse(app.proxy.public_url).host
|
return Server.from_url(app.proxy.public_url).host
|
||||||
|
|
||||||
|
|
||||||
def public_url(app, user_or_service=None, path=''):
|
def public_url(app, user_or_service=None, path=''):
|
||||||
@@ -220,7 +220,7 @@ def public_url(app, user_or_service=None, path=''):
|
|||||||
prefix = user_or_service.server.base_url
|
prefix = user_or_service.server.base_url
|
||||||
else:
|
else:
|
||||||
host = public_host(app)
|
host = public_host(app)
|
||||||
prefix = app.proxy.public_server.base_url
|
prefix = Server.from_url(app.proxy.public_url).base_url
|
||||||
if path:
|
if path:
|
||||||
return host + url_path_join(prefix, path)
|
return host + url_path_join(prefix, path)
|
||||||
else:
|
else:
|
||||||
|
@@ -7,6 +7,7 @@ import pytest
|
|||||||
from tornado import gen
|
from tornado import gen
|
||||||
|
|
||||||
from .. import orm
|
from .. import orm
|
||||||
|
from .. import objects
|
||||||
from ..user import User
|
from ..user import User
|
||||||
from .mocking import MockSpawner
|
from .mocking import MockSpawner
|
||||||
|
|
||||||
@@ -20,6 +21,9 @@ def test_server(db):
|
|||||||
assert server.proto == 'http'
|
assert server.proto == 'http'
|
||||||
assert isinstance(server.port, int)
|
assert isinstance(server.port, int)
|
||||||
assert isinstance(server.cookie_name, str)
|
assert isinstance(server.cookie_name, str)
|
||||||
|
|
||||||
|
# test wrapper
|
||||||
|
server = objects.Server(orm_server=server)
|
||||||
assert server.host == 'http://127.0.0.1:%i' % server.port
|
assert server.host == 'http://127.0.0.1:%i' % server.port
|
||||||
assert server.url == server.host + '/'
|
assert server.url == server.host + '/'
|
||||||
assert server.bind_url == 'http://*:%i/' % server.port
|
assert server.bind_url == 'http://*:%i/' % server.port
|
||||||
@@ -29,9 +33,9 @@ def test_server(db):
|
|||||||
|
|
||||||
|
|
||||||
def test_user(db):
|
def test_user(db):
|
||||||
user = orm.User(name='kaylee',
|
user = User(orm.User(name='kaylee',
|
||||||
state={'pid': 4234},
|
state={'pid': 4234},
|
||||||
)
|
))
|
||||||
server = orm.Server()
|
server = orm.Server()
|
||||||
user.servers.append(server)
|
user.servers.append(server)
|
||||||
db.add(user)
|
db.add(user)
|
||||||
|
@@ -17,6 +17,7 @@ import requests
|
|||||||
from tornado import gen
|
from tornado import gen
|
||||||
|
|
||||||
from ..user import User
|
from ..user import User
|
||||||
|
from ..objects import Hub
|
||||||
from .. import spawner as spawnermod
|
from .. import spawner as spawnermod
|
||||||
from ..spawner import LocalProcessSpawner
|
from ..spawner import LocalProcessSpawner
|
||||||
from .. import orm
|
from .. import orm
|
||||||
@@ -43,8 +44,8 @@ def setup():
|
|||||||
|
|
||||||
def new_spawner(db, **kwargs):
|
def new_spawner(db, **kwargs):
|
||||||
kwargs.setdefault('cmd', [sys.executable, '-c', _echo_sleep])
|
kwargs.setdefault('cmd', [sys.executable, '-c', _echo_sleep])
|
||||||
|
kwargs.setdefault('hub', Hub())
|
||||||
kwargs.setdefault('user', User(db.query(orm.User).first(), {}))
|
kwargs.setdefault('user', User(db.query(orm.User).first(), {}))
|
||||||
kwargs.setdefault('hub', db.query(orm.Hub).first())
|
|
||||||
kwargs.setdefault('notebook_dir', os.getcwd())
|
kwargs.setdefault('notebook_dir', os.getcwd())
|
||||||
kwargs.setdefault('default_url', '/user/{username}/lab')
|
kwargs.setdefault('default_url', '/user/{username}/lab')
|
||||||
kwargs.setdefault('INTERRUPT_TIMEOUT', 1)
|
kwargs.setdefault('INTERRUPT_TIMEOUT', 1)
|
||||||
|
@@ -9,9 +9,10 @@ from sqlalchemy import inspect
|
|||||||
from tornado import gen
|
from tornado import gen
|
||||||
from tornado.log import app_log
|
from tornado.log import app_log
|
||||||
|
|
||||||
from .utils import url_path_join, default_server_name, new_token
|
from .utils import url_path_join, default_server_name
|
||||||
|
|
||||||
from . import orm
|
from . import orm
|
||||||
|
from .objects import Server
|
||||||
from traitlets import HasTraits, Any, Dict, observe, default
|
from traitlets import HasTraits, Any, Dict, observe, default
|
||||||
from .spawner import LocalProcessSpawner
|
from .spawner import LocalProcessSpawner
|
||||||
|
|
||||||
@@ -157,6 +158,13 @@ class User(HasTraits):
|
|||||||
if self.server is None:
|
if self.server is None:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def server(self):
|
||||||
|
if len(self.servers) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return Server(orm_server=self.servers[0])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def escaped_name(self):
|
def escaped_name(self):
|
||||||
@@ -228,14 +236,13 @@ class User(HasTraits):
|
|||||||
cookie_name=self.cookie_name,
|
cookie_name=self.cookie_name,
|
||||||
base_url=base_url,
|
base_url=base_url,
|
||||||
)
|
)
|
||||||
db.add(orm_server)
|
self.servers.append(orm_server)
|
||||||
db.commit()
|
|
||||||
server = Server(orm_server=orm_server)
|
|
||||||
self.servers.append(server)
|
|
||||||
|
|
||||||
api_token = self.new_api_token()
|
api_token = self.new_api_token()
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
server = Server(orm_server=orm_server)
|
||||||
|
|
||||||
spawner = self.spawner
|
spawner = self.spawner
|
||||||
# Passing server_name to the spawner
|
# Passing server_name to the spawner
|
||||||
spawner.server_name = server_name
|
spawner.server_name = server_name
|
||||||
@@ -279,7 +286,7 @@ class User(HasTraits):
|
|||||||
ip_port = 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:
|
if ip_port:
|
||||||
# get ip, port info from return value of start()
|
# get ip, port info from return value of start()
|
||||||
self.server.ip, self.server.port = ip_port
|
server.ip, server.port = ip_port
|
||||||
else:
|
else:
|
||||||
# prior to 0.7, spawners had to store this info in user.server themselves.
|
# 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.
|
# Handle < 0.7 behavior with a warning, assuming info was stored in db by the Spawner.
|
||||||
@@ -317,14 +324,14 @@ class User(HasTraits):
|
|||||||
db.commit()
|
db.commit()
|
||||||
self.waiting_for_response = True
|
self.waiting_for_response = True
|
||||||
try:
|
try:
|
||||||
yield self.server.wait_up(http=True, timeout=spawner.http_timeout)
|
yield server.wait_up(http=True, timeout=spawner.http_timeout)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, TimeoutError):
|
if isinstance(e, TimeoutError):
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"{user}'s server never showed up at {url} "
|
"{user}'s server never showed up at {url} "
|
||||||
"after {http_timeout} seconds. Giving up".format(
|
"after {http_timeout} seconds. Giving up".format(
|
||||||
user=self.name,
|
user=self.name,
|
||||||
url=self.server.url,
|
url=server.url,
|
||||||
http_timeout=spawner.http_timeout,
|
http_timeout=spawner.http_timeout,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -332,7 +339,7 @@ class User(HasTraits):
|
|||||||
else:
|
else:
|
||||||
e.reason = 'error'
|
e.reason = 'error'
|
||||||
self.log.error("Unhandled error waiting for {user}'s server to show up at {url}: {error}".format(
|
self.log.error("Unhandled error waiting for {user}'s server to show up at {url}: {error}".format(
|
||||||
user=self.name, url=self.server.url, error=e,
|
user=self.name, url=server.url, error=e,
|
||||||
))
|
))
|
||||||
try:
|
try:
|
||||||
yield self.stop()
|
yield self.stop()
|
||||||
|
Reference in New Issue
Block a user