mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-15 22:13:00 +00:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f7903a78d1 | ||
![]() |
edddacec1d | ||
![]() |
c5a33f227f | ||
![]() |
aaa20e5787 | ||
![]() |
c8ebf1ec90 | ||
![]() |
89745c002b | ||
![]() |
3e0ee49ce8 | ||
![]() |
bbbeffb443 | ||
![]() |
c29a5ca4ce | ||
![]() |
f5182fe349 |
@@ -4,14 +4,14 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 20.8b1
|
rev: 22.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: v2.2.1
|
rev: v2.2.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: "3.8.4"
|
rev: "3.8.4"
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
|
@@ -3,7 +3,7 @@ swagger: "2.0"
|
|||||||
info:
|
info:
|
||||||
title: JupyterHub
|
title: JupyterHub
|
||||||
description: The REST API for JupyterHub
|
description: The REST API for JupyterHub
|
||||||
version: 1.5.0
|
version: 1.5.1
|
||||||
license:
|
license:
|
||||||
name: BSD-3-Clause
|
name: BSD-3-Clause
|
||||||
schemes: [http, https]
|
schemes: [http, https]
|
||||||
|
@@ -17,6 +17,22 @@ A few fully backward-compatible features have been backported from 2.0.
|
|||||||
|
|
||||||
[ghsa-cw7p-q79f-m2v7]: https://github.com/jupyterhub/jupyterhub/security/advisories/GHSA-cw7p-q79f-m2v7
|
[ghsa-cw7p-q79f-m2v7]: https://github.com/jupyterhub/jupyterhub/security/advisories/GHSA-cw7p-q79f-m2v7
|
||||||
|
|
||||||
|
### 1.5.1 2022-12-01
|
||||||
|
|
||||||
|
This is a patch release, improving db resiliency when certain errors occur, without requiring a jupyterhub restart.
|
||||||
|
|
||||||
|
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.5.0...1.5.1))
|
||||||
|
|
||||||
|
#### Merged PRs
|
||||||
|
|
||||||
|
- Backport db rollback fixes to 1.x [#4076](https://github.com/jupyterhub/jupyterhub/pull/4076) ([@mriedem](https://github.com/mriedem), [@minrk](https://github.com/minrk)), [@nsshah1288](https://github.com/nsshah1288)
|
||||||
|
|
||||||
|
#### Contributors to this release
|
||||||
|
|
||||||
|
([GitHub contributors page for this release](https://github.com/jupyterhub/jupyterhub/graphs/contributors?from=2022-09-09&to=2022-11-30&type=c))
|
||||||
|
|
||||||
|
[@mriedem](https://github.com/mriedem) | [@minrk](https://github.com/minrk) | [@nsshah1288](https://github.com/nsshah1288)
|
||||||
|
|
||||||
### [1.5.0] 2021-11-04
|
### [1.5.0] 2021-11-04
|
||||||
|
|
||||||
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.4.2...1.5.0))
|
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.4.2...1.5.0))
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
version_info = (
|
version_info = (
|
||||||
1,
|
1,
|
||||||
5,
|
5,
|
||||||
0,
|
1,
|
||||||
"", # release (b1, rc1, or "" for final or dev)
|
"", # release (b1, rc1, or "" for final or dev)
|
||||||
# "dev", # dev or nothing for beta/rc/stable releases
|
# "dev", # dev or nothing for beta/rc/stable releases
|
||||||
)
|
)
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import atexit
|
import atexit
|
||||||
import binascii
|
import binascii
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -90,6 +89,7 @@ from .pagination import Pagination
|
|||||||
from .proxy import Proxy, ConfigurableHTTPProxy
|
from .proxy import Proxy, ConfigurableHTTPProxy
|
||||||
from .traitlets import URLPrefix, Command, EntryPointType, Callable
|
from .traitlets import URLPrefix, Command, EntryPointType, Callable
|
||||||
from .utils import (
|
from .utils import (
|
||||||
|
catch_db_error,
|
||||||
maybe_future,
|
maybe_future,
|
||||||
url_path_join,
|
url_path_join,
|
||||||
print_stacks,
|
print_stacks,
|
||||||
@@ -2000,6 +2000,7 @@ class JupyterHub(Application):
|
|||||||
# purge expired tokens hourly
|
# purge expired tokens hourly
|
||||||
purge_expired_tokens_interval = 3600
|
purge_expired_tokens_interval = 3600
|
||||||
|
|
||||||
|
@catch_db_error
|
||||||
def purge_expired_tokens(self):
|
def purge_expired_tokens(self):
|
||||||
"""purge all expiring token objects from the database
|
"""purge all expiring token objects from the database
|
||||||
|
|
||||||
@@ -2015,7 +2016,7 @@ class JupyterHub(Application):
|
|||||||
await self._add_tokens(self.service_tokens, kind='service')
|
await self._add_tokens(self.service_tokens, kind='service')
|
||||||
await self._add_tokens(self.api_tokens, kind='user')
|
await self._add_tokens(self.api_tokens, kind='user')
|
||||||
|
|
||||||
self.purge_expired_tokens()
|
await self.purge_expired_tokens()
|
||||||
# purge expired tokens hourly
|
# purge expired tokens hourly
|
||||||
# we don't need to be prompt about this
|
# we don't need to be prompt about this
|
||||||
# because expired tokens cannot be used anyway
|
# because expired tokens cannot be used anyway
|
||||||
@@ -2663,6 +2664,7 @@ class JupyterHub(Application):
|
|||||||
with open(self.config_file, mode='w') as f:
|
with open(self.config_file, mode='w') as f:
|
||||||
f.write(config_text)
|
f.write(config_text)
|
||||||
|
|
||||||
|
@catch_db_error
|
||||||
async def update_last_activity(self):
|
async def update_last_activity(self):
|
||||||
"""Update User.last_activity timestamps from the proxy"""
|
"""Update User.last_activity timestamps from the proxy"""
|
||||||
routes = await self.proxy.get_all_routes()
|
routes = await self.proxy.get_all_routes()
|
||||||
|
@@ -81,9 +81,14 @@ class BaseHandler(RequestHandler):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await self.get_current_user()
|
await self.get_current_user()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
self.log.exception("Failed to get current user")
|
# ensure get_current_user is never called again for this handler,
|
||||||
|
# since it failed
|
||||||
self._jupyterhub_user = None
|
self._jupyterhub_user = None
|
||||||
|
self.log.exception("Failed to get current user")
|
||||||
|
if isinstance(e, SQLAlchemyError):
|
||||||
|
self.log.error("Rolling back session due to database error")
|
||||||
|
self.db.rollback()
|
||||||
|
|
||||||
return await maybe_future(super().prepare())
|
return await maybe_future(super().prepare())
|
||||||
|
|
||||||
@@ -426,7 +431,8 @@ class BaseHandler(RequestHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
# don't let errors here raise more than once
|
# don't let errors here raise more than once
|
||||||
self._jupyterhub_user = None
|
self._jupyterhub_user = None
|
||||||
self.log.exception("Error getting current user")
|
# but still raise, which will get handled in .prepare()
|
||||||
|
raise
|
||||||
return self._jupyterhub_user
|
return self._jupyterhub_user
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -1499,14 +1505,10 @@ class UserUrlHandler(BaseHandler):
|
|||||||
|
|
||||||
# if request is expecting JSON, assume it's an API request and fail with 503
|
# if request is expecting JSON, assume it's an API request and fail with 503
|
||||||
# because it won't like the redirect to the pending page
|
# because it won't like the redirect to the pending page
|
||||||
if (
|
if get_accepted_mimetype(
|
||||||
get_accepted_mimetype(
|
|
||||||
self.request.headers.get('Accept', ''),
|
self.request.headers.get('Accept', ''),
|
||||||
choices=['application/json', 'text/html'],
|
choices=['application/json', 'text/html'],
|
||||||
)
|
) == 'application/json' or 'api' in user_path.split('/'):
|
||||||
== 'application/json'
|
|
||||||
or 'api' in user_path.split('/')
|
|
||||||
):
|
|
||||||
self._fail_api_request(user_name, server_name)
|
self._fail_api_request(user_name, server_name)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@@ -4,9 +4,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import errno
|
import errno
|
||||||
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
import socket
|
import socket
|
||||||
@@ -22,6 +22,7 @@ from hmac import compare_digest
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from async_generator import aclosing
|
from async_generator import aclosing
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from tornado import ioloop
|
from tornado import ioloop
|
||||||
from tornado import web
|
from tornado import web
|
||||||
from tornado.httpclient import AsyncHTTPClient
|
from tornado.httpclient import AsyncHTTPClient
|
||||||
@@ -642,3 +643,21 @@ def get_accepted_mimetype(accept_header, choices=None):
|
|||||||
else:
|
else:
|
||||||
return mime
|
return mime
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def catch_db_error(f):
|
||||||
|
"""Catch and rollback database errors"""
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
async def catching(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
r = f(self, *args, **kwargs)
|
||||||
|
if inspect.isawaitable(r):
|
||||||
|
r = await r
|
||||||
|
except SQLAlchemyError:
|
||||||
|
self.log.exception("Rolling back session due to database error")
|
||||||
|
self.db.rollback()
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
|
return catching
|
||||||
|
@@ -26,8 +26,8 @@ import os
|
|||||||
import pipes
|
import pipes
|
||||||
import shutil
|
import shutil
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from distutils.version import LooseVersion as V
|
|
||||||
|
|
||||||
|
from distutils.version import LooseVersion as V
|
||||||
from invoke import run as invoke_run
|
from invoke import run as invoke_run
|
||||||
from invoke import task
|
from invoke import task
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user