Compare commits

...

10 Commits
2.1.1 ... 0.9.4

Author SHA1 Message Date
Min RK
b1111363fd release 0.9.4 2018-09-24 13:02:36 +02:00
Min RK
6c99b807c2 update changelog for 0.9.4 2018-09-24 13:00:27 +02:00
Min RK
8d650f594e changelog for 0.9.4 2018-09-24 12:58:16 +02:00
Min RK
04a0a3a2e5 fix oauth client cleanup
- delete oauth clients for servers when they shutdown
- avoid deleting oauth clients for servers still running across an 0.8 -> 0.9 upgrade, when the oauth client ids changed from `user-NAME` to `jupyterhub-user-NAME`
2018-09-24 12:58:10 +02:00
Min RK
9cebfd6367 Fix content-type on API endpoints
and includes content-type header checks in tests to catch regressions
2018-09-24 12:57:26 +02:00
Min RK
587cd70221 omit pdf builds on rtd due to bug in sphinx 2018-09-24 12:57:01 +02:00
Min RK
e94f5e043a release 0.9.3 2018-09-12 09:46:02 +02:00
Min RK
5456fb6356 remove spurious print from keepalive code
and send keepalive every 8 seconds

to protect against possibly aggressive proxies dropping connections after 10 seconds of inactivity
2018-09-12 09:46:02 +02:00
Min RK
fb75b9a392 write needs no await 2018-09-11 16:42:29 +02:00
Min RK
90d341e6f7 changelog for 0.9.3
Mainly small fixes, but the token page could be completely broken

This release will include the spawner.handler addition,
but not the oauthlib change currently in master
2018-09-11 16:42:21 +02:00
9 changed files with 79 additions and 9 deletions

View File

@@ -3,7 +3,7 @@ swagger: '2.0'
info:
title: JupyterHub
description: The REST API for JupyterHub
version: 0.9.0dev
version: 0.9.4
license:
name: BSD-3-Clause
schemes:

View File

@@ -9,6 +9,30 @@ command line for details.
## 0.9
### [0.9.4] 2018-09-24
JupyterHub 0.9.4 is a small bugfix release.
- Fixes an issue that required all running user servers to be restarted
when performing an upgrade from 0.8 to 0.9.
- Fixes content-type for API endpoints back to `application/json`.
It was `text/html` in 0.9.0-0.9.3.
### [0.9.3] 2018-09-12
JupyterHub 0.9.3 contains small bugfixes and improvements
- Fix token page and model handling of `expires_at`.
This field was missing from the REST API model for tokens
and could cause the token page to not render
- Add keep-alive to progress event stream to avoid proxies dropping
the connection due to inactivity
- Documentation and example improvements
- Disable quit button when using notebook 5.6
- Prototype new feature (may change prior to 1.0):
pass requesting Handler to Spawners during start,
accessible as `self.handler`
### [0.9.2] 2018-08-10
JupyterHub 0.9.2 contains small bugfixes and improvements.
@@ -402,7 +426,9 @@ Fix removal of `/login` page in 0.4.0, breaking some OAuth providers.
First preview release
[Unreleased]: https://github.com/jupyterhub/jupyterhub/compare/0.9.2...HEAD
[Unreleased]: https://github.com/jupyterhub/jupyterhub/compare/0.9.4...HEAD
[0.9.4]: https://github.com/jupyterhub/jupyterhub/compare/0.9.3...0.9.4
[0.9.3]: https://github.com/jupyterhub/jupyterhub/compare/0.9.2...0.9.3
[0.9.2]: https://github.com/jupyterhub/jupyterhub/compare/0.9.1...0.9.2
[0.9.1]: https://github.com/jupyterhub/jupyterhub/compare/0.9.0...0.9.1
[0.9.0]: https://github.com/jupyterhub/jupyterhub/compare/0.8.1...0.9.0

View File

@@ -4,11 +4,11 @@
# Distributed under the terms of the Modified BSD License.
version_info = (
1,
0,
0,
9,
4,
"", # release (b1, rc1, or "" for final or dev)
"dev", # dev or nothing
# "dev", # dev or nothing
)
# pep 440 version: no dot before beta/rc, but before .dev

View File

@@ -30,6 +30,9 @@ class APIHandler(BaseHandler):
def content_security_policy(self):
return '; '.join([super().content_security_policy, "default-src 'none'"])
def get_content_type(self):
return 'application/json'
def check_referer(self):
"""Check Origin for cross-site API requests.
@@ -265,3 +268,13 @@ class APIHandler(BaseHandler):
def options(self, *args, **kwargs):
self.finish()
class API404(APIHandler):
"""404 for API requests
Ensures JSON 404 errors for malformed URLs
"""
async def prepare(self):
await super().prepare()
raise web.HTTPError(404)

View File

@@ -429,7 +429,7 @@ class UserAdminAccessAPIHandler(APIHandler):
class SpawnProgressAPIHandler(APIHandler):
"""EventStream handler for pending spawns"""
keepalive_interval = 10
keepalive_interval = 8
def get_content_type(self):
return 'text/event-stream'
@@ -445,7 +445,6 @@ class SpawnProgressAPIHandler(APIHandler):
_finished = False
def on_finish(self):
print("on finish")
self._finished = True
async def keepalive(self):
@@ -456,7 +455,7 @@ class SpawnProgressAPIHandler(APIHandler):
"""
while not self._finished:
try:
await self.write("\n\n")
self.write("\n\n")
except (StreamClosedError, RuntimeError):
return
await asyncio.sleep(self.keepalive_interval)

View File

@@ -973,6 +973,8 @@ class JupyterHub(Application):
h.extend(self.extra_handlers)
h.append((r'/logo', LogoHandler, {'path': self.logo_file}))
h.append((r'/api/(.*)', apihandlers.base.API404))
self.handlers = self.add_url_prefix(self.hub_prefix, h)
# some extra handlers, outside hub_prefix
self.handlers.extend([
@@ -1506,6 +1508,10 @@ class JupyterHub(Application):
for user in self.users.values():
for spawner in user.spawners.values():
oauth_client_ids.add(spawner.oauth_client_id)
# avoid deleting clients created by 0.8
# 0.9 uses `jupyterhub-user-...` for the client id, while
# 0.8 uses just `user-...`
oauth_client_ids.add(spawner.oauth_client_id.split('-', 1)[1])
client_store = self.oauth_provider.client_authenticator.client_store
for i, oauth_client in enumerate(self.db.query(orm.OAuthClient)):

View File

@@ -100,6 +100,8 @@ def api_request(app, *api_path, **kwargs):
assert "frame-ancestors 'self'" in resp.headers['Content-Security-Policy']
assert ujoin(app.hub.base_url, "security/csp-report") in resp.headers['Content-Security-Policy']
assert 'http' not in resp.headers['Content-Security-Policy']
if not kwargs.get('stream', False) and resp.content:
assert resp.headers.get('content-type') == 'application/json'
return resp
@@ -746,6 +748,8 @@ def test_progress(request, app, no_patience, slow_spawn):
r = yield api_request(app, 'users', name, 'server/progress', stream=True)
r.raise_for_status()
request.addfinalizer(r.close)
assert r.headers['content-type'] == 'text/event-stream'
ex = async_requests.executor
line_iter = iter(r.iter_lines(decode_unicode=True))
evt = yield ex.submit(next_event, line_iter)
@@ -807,6 +811,7 @@ def test_progress_ready(request, app):
r = yield api_request(app, 'users', name, 'server/progress', stream=True)
r.raise_for_status()
request.addfinalizer(r.close)
assert r.headers['content-type'] == 'text/event-stream'
ex = async_requests.executor
line_iter = iter(r.iter_lines(decode_unicode=True))
evt = yield ex.submit(next_event, line_iter)
@@ -826,6 +831,7 @@ def test_progress_bad(request, app, no_patience, bad_spawn):
r = yield api_request(app, 'users', name, 'server/progress', stream=True)
r.raise_for_status()
request.addfinalizer(r.close)
assert r.headers['content-type'] == 'text/event-stream'
ex = async_requests.executor
line_iter = iter(r.iter_lines(decode_unicode=True))
evt = yield ex.submit(next_event, line_iter)
@@ -847,6 +853,7 @@ def test_progress_bad_slow(request, app, no_patience, slow_bad_spawn):
r = yield api_request(app, 'users', name, 'server/progress', stream=True)
r.raise_for_status()
request.addfinalizer(r.close)
assert r.headers['content-type'] == 'text/event-stream'
ex = async_requests.executor
line_iter = iter(r.iter_lines(decode_unicode=True))
evt = yield ex.submit(next_event, line_iter)

View File

@@ -558,11 +558,25 @@ class User:
# remove server entry from db
spawner.server = None
if not spawner.will_resume:
# find and remove the API token if the spawner isn't
# find and remove the API token and oauth client if the spawner isn't
# going to re-use it next time
orm_token = orm.APIToken.find(self.db, api_token)
if orm_token:
self.db.delete(orm_token)
# remove oauth client as well
# handle upgrades from 0.8, where client id will be `user-USERNAME`,
# not just `jupyterhub-user-USERNAME`
client_ids = (
spawner.oauth_client_id,
spawner.oauth_client_id.split('-', 1)[1],
)
for oauth_client in (
self.db
.query(orm.OAuthClient)
.filter(orm.OAuthClient.identifier.in_(client_ids))
):
self.log.debug("Deleting oauth client %s", oauth_client.identifier)
self.db.delete(oauth_client)
self.db.commit()
finally:
spawner.orm_spawner.started = None

View File

@@ -4,3 +4,8 @@ conda:
file: docs/environment.yml
python:
version: 3
formats:
- htmlzip
- epub
# pdf disabled due to bug in sphinx 1.8 + recommonmark
# - pdf