Compare commits

...

16 Commits
2.3.0 ... 2.3.1

Author SHA1 Message Date
Erik Sundell
20b3229249 Bump to 2.3.1 2022-06-06 16:26:12 +02:00
Yuvi Panda
f0862f1d10 Merge pull request #3930 from consideRatio/pr/add-changelog-to-2x-branch
Add changelog for 2.3.1
2022-06-06 19:53:45 +05:30
Erik Sundell
3c5f9b255e Add changelog for 2.3.1 2022-06-06 16:15:36 +02:00
Erik Sundell
b6d9d5c120 Merge pull request #3926 from meeseeksmachine/auto-backport-of-pr-3910-on-2.x
Backport PR #3910 on branch 2.x (use equality to filter token prefixes)
2022-06-06 15:36:36 +02:00
Yuvi Panda
bccd0e2ff1 Merge pull request #3928 from yuvipanda/auto-backport-of-pr-3919-on-2.x
Auto backport of pr 3919 on 2.x
2022-06-06 18:40:16 +05:30
Yuvi Panda
a2d39c693d Merge pull request #3927 from meeseeksmachine/auto-backport-of-pr-3918-on-2.x
Backport PR #3918 on branch 2.x (set default_url via config)
2022-06-06 18:40:06 +05:30
Yuvi Panda
76e65da9ff Merge pull request #3925 from meeseeksmachine/auto-backport-of-pr-3906-on-2.x
Backport PR #3906 on branch 2.x (Force add existing certificates)
2022-06-06 18:39:24 +05:30
Yuvi Panda
eb9bb71655 Merge pull request #3924 from meeseeksmachine/auto-backport-of-pr-3889-on-2.x
Backport PR #3889 on branch 2.x (admin: make user-info table selectable)
2022-06-06 18:39:12 +05:30
Yuvi Panda
a39ef8f163 Merge pull request #3923 from meeseeksmachine/auto-backport-of-pr-3837-on-2.x
Backport PR #3837 on branch 2.x (ensure _import_error is set when JUPYTERHUB_SINGLEUSER_APP is unavailable)
2022-06-06 18:38:57 +05:30
Yuvi Panda
f4727cba47 Backport PR #3919: ensure custom template is loaded with jupyter-server notebook extension 2022-06-03 21:15:44 +05:30
Yuvi Panda
14dfa65c75 Backport PR #3918: set default_url via config 2022-06-03 15:17:09 +00:00
Yuvi Panda
9f23bc2959 Backport PR #3910: use equality to filter token prefixes 2022-06-03 15:17:01 +00:00
Min RK
24e8362401 Backport PR #3906: Force add existing certificates 2022-06-03 15:16:46 +00:00
Min RK
c4c662843c Backport PR #3889: admin: make user-info table selectable 2022-06-03 15:16:29 +00:00
Erik Sundell
6d5b13962c Backport PR #3837: ensure _import_error is set when JUPYTERHUB_SINGLEUSER_APP is unavailable 2022-06-03 15:16:20 +00:00
Min RK
fe64595d75 Bump to 2.3.1.dev 2022-05-06 16:06:06 +02:00
11 changed files with 105 additions and 47 deletions

View File

@@ -6,7 +6,7 @@ info:
description: The REST API for JupyterHub description: The REST API for JupyterHub
license: license:
name: BSD-3-Clause name: BSD-3-Clause
version: 2.3.0 version: 2.3.1
servers: servers:
- url: /hub/api - url: /hub/api
security: security:

File diff suppressed because one or more lines are too long

View File

@@ -200,6 +200,25 @@ const ServerDashboard = (props) => {
); );
}; };
const ServerRowTable = ({ data }) => {
return (
<ReactObjectTableViewer
className="table-striped table-bordered"
style={{
padding: "3px 6px",
margin: "auto",
}}
keyStyle={{
padding: "4px",
}}
valueStyle={{
padding: "4px",
}}
data={data}
/>
);
};
const serverRow = (user, server) => { const serverRow = (user, server) => {
const { servers, ...userNoServers } = user; const { servers, ...userNoServers } = user;
const serverNameDash = server.name ? `-${server.name}` : ""; const serverNameDash = server.name ? `-${server.name}` : "";
@@ -286,37 +305,11 @@ const ServerDashboard = (props) => {
> >
<Card style={{ width: "100%", padding: 3, margin: "0 auto" }}> <Card style={{ width: "100%", padding: 3, margin: "0 auto" }}>
<Card.Title>User</Card.Title> <Card.Title>User</Card.Title>
<ReactObjectTableViewer <ServerRowTable data={userNoServers} />
className="table-striped table-bordered admin-table-head"
style={{
padding: "3px 6px",
margin: "auto",
}}
keyStyle={{
padding: "4px",
}}
valueStyle={{
padding: "4px",
}}
data={userNoServers}
/>
</Card> </Card>
<Card style={{ width: "100%", padding: 3, margin: "0 auto" }}> <Card style={{ width: "100%", padding: 3, margin: "0 auto" }}>
<Card.Title>Server</Card.Title> <Card.Title>Server</Card.Title>
<ReactObjectTableViewer <ServerRowTable data={server} />
className="table-striped table-bordered admin-table-head"
style={{
padding: "3px 6px",
margin: "auto",
}}
keyStyle={{
padding: "4px",
}}
valueStyle={{
padding: "4px",
}}
data={server}
/>
</Card> </Card>
</CardGroup> </CardGroup>
</Collapse> </Collapse>

View File

@@ -2,7 +2,7 @@
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
# version_info updated by running `tbump` # version_info updated by running `tbump`
version_info = (2, 3, 0, "", "") version_info = (2, 3, 1, "", "")
# pep 440 version: no dot before beta/rc, but before .dev # pep 440 version: no dot before beta/rc, but before .dev
# 0.1.0rc1 # 0.1.0rc1

View File

@@ -1689,7 +1689,9 @@ class JupyterHub(Application):
for authority, files in self.internal_ssl_authorities.items(): for authority, files in self.internal_ssl_authorities.items():
if files: if files:
self.log.info("Adding CA for %s", authority) self.log.info("Adding CA for %s", authority)
certipy.store.add_record(authority, is_ca=True, files=files) certipy.store.add_record(
authority, is_ca=True, files=files, overwrite=True
)
self.internal_trust_bundles = certipy.trust_from_graph( self.internal_trust_bundles = certipy.trust_from_graph(
self.internal_ssl_components_trust self.internal_ssl_components_trust

View File

@@ -536,9 +536,7 @@ class Hashed(Expiring):
prefix = token[: cls.prefix_length] prefix = token[: cls.prefix_length]
# since we can't filter on hashed values, filter on prefix # since we can't filter on hashed values, filter on prefix
# so we aren't comparing with all tokens # so we aren't comparing with all tokens
prefix_match = db.query(cls).filter( prefix_match = db.query(cls).filter_by(prefix=prefix)
bindparam('prefix', prefix).startswith(cls.prefix)
)
prefix_match = prefix_match.filter( prefix_match = prefix_match.filter(
or_(cls.expires_at == None, cls.expires_at >= cls.now()) or_(cls.expires_at == None, cls.expires_at >= cls.now())
) )

View File

@@ -29,9 +29,9 @@ else:
try: try:
App = import_item(JUPYTERHUB_SINGLEUSER_APP) App = import_item(JUPYTERHUB_SINGLEUSER_APP)
except ImportError as e: except ImportError as e:
continue
if _import_error is None: if _import_error is None:
_import_error = e _import_error = e
continue
else: else:
break break
if App is None: if App is None:

View File

@@ -182,6 +182,7 @@ page_template = """
<span> <span>
<a href='{{hub_control_panel_url}}' <a href='{{hub_control_panel_url}}'
id='jupyterhub-control-panel-link'
class='btn btn-default btn-sm navbar-btn pull-right' class='btn btn-default btn-sm navbar-btn pull-right'
style='margin-right: 4px; margin-left: 2px;'> style='margin-right: 4px; margin-left: 2px;'>
Control Panel Control Panel
@@ -633,8 +634,15 @@ class SingleUserNotebookAppMixin(Configurable):
# disable trash by default # disable trash by default
# this can be re-enabled by config # this can be re-enabled by config
self.config.FileContentsManager.delete_to_trash = False self.config.FileContentsManager.delete_to_trash = False
# load default-url env at higher priority than `@default`,
# which may have their own _defaults_ which should not override explicit default_url config
# via e.g. c.Spawner.default_url. Seen in jupyterlab's SingleUserLabApp.
default_url = os.environ.get("JUPYTERHUB_DEFAULT_URL")
if default_url:
self.config[self.__class__.__name__].default_url = default_url
self._log_app_versions() self._log_app_versions()
return super().initialize(argv) super().initialize(argv)
self.patch_templates()
def start(self): def start(self):
self.log.info("Starting jupyterhub-singleuser server version %s", __version__) self.log.info("Starting jupyterhub-singleuser server version %s", __version__)
@@ -705,7 +713,6 @@ class SingleUserNotebookAppMixin(Configurable):
# apply X-JupyterHub-Version to *all* request handlers (even redirects) # apply X-JupyterHub-Version to *all* request handlers (even redirects)
self.patch_default_headers() self.patch_default_headers()
self.patch_templates()
def page_config_hook(self, handler, page_config): def page_config_hook(self, handler, page_config):
"""JupyterLab page config hook """JupyterLab page config hook
@@ -738,19 +745,30 @@ class SingleUserNotebookAppMixin(Configurable):
) )
self.jinja_template_vars['hub_host'] = self.hub_host self.jinja_template_vars['hub_host'] = self.hub_host
self.jinja_template_vars['hub_prefix'] = self.hub_prefix self.jinja_template_vars['hub_prefix'] = self.hub_prefix
env = self.web_app.settings['jinja2_env'] self.jinja_template_vars[
'hub_control_panel_url'
] = self.hub_host + url_path_join(self.hub_prefix, 'home')
env.globals['hub_control_panel_url'] = self.hub_host + url_path_join( settings = self.web_app.settings
self.hub_prefix, 'home' # patch classic notebook jinja env
) jinja_envs = []
if 'jinja2_env' in settings:
# default jinja env (should we do this on jupyter-server, or only notebook?)
jinja_envs.append(settings['jinja2_env'])
if 'notebook_jinja2_env' in settings:
# when running with jupyter-server, classic notebook (nbclassic server extension)
# gets its own jinja env, which needs the same patch
jinja_envs.append(settings['notebook_jinja2_env'])
# patch jinja env loading to modify page template # patch jinja env loading to get modified template, only for base page.html
def get_page(name): def get_page(name):
if name == 'page.html': if name == 'page.html':
return page_template return page_template
orig_loader = env.loader for jinja_env in jinja_envs:
env.loader = ChoiceLoader([FunctionLoader(get_page), orig_loader]) jinja_env.loader = ChoiceLoader(
[FunctionLoader(get_page), jinja_env.loader]
)
def load_server_extensions(self): def load_server_extensions(self):
# Loading LabApp sets $JUPYTERHUB_API_TOKEN on load, which is incorrect # Loading LabApp sets $JUPYTERHUB_API_TOKEN on load, which is incorrect

View File

@@ -5,9 +5,11 @@ from contextlib import contextmanager
from subprocess import CalledProcessError from subprocess import CalledProcessError
from subprocess import check_output from subprocess import check_output
from unittest import mock from unittest import mock
from urllib.parse import urlencode
from urllib.parse import urlparse from urllib.parse import urlparse
import pytest import pytest
from bs4 import BeautifulSoup
import jupyterhub import jupyterhub
from .. import orm from .. import orm
@@ -16,6 +18,7 @@ from .mocking import public_url
from .mocking import StubSingleUserSpawner from .mocking import StubSingleUserSpawner
from .utils import async_requests from .utils import async_requests
from .utils import AsyncSession from .utils import AsyncSession
from .utils import get_page
@contextmanager @contextmanager
@@ -225,3 +228,22 @@ def test_singleuser_app_class(JUPYTERHUB_SINGLEUSER_APP):
else: else:
assert '--ServerApp.' in out assert '--ServerApp.' in out
assert '--NotebookApp.' not in out assert '--NotebookApp.' not in out
async def test_nbclassic_control_panel(app, user):
# use StubSingleUserSpawner to launch a single-user app in a thread
app.spawner_class = StubSingleUserSpawner
app.tornado_settings['spawner_class'] = StubSingleUserSpawner
# login, start the server
await user.spawn()
cookies = await app.login_user(user.name)
next_url = url_path_join(user.url, "tree/")
url = '/?' + urlencode({'next': next_url})
r = await get_page(url, app, cookies=cookies)
r.raise_for_status()
assert urlparse(r.url).path == urlparse(next_url).path
page = BeautifulSoup(r.text, "html.parser")
link = page.find("a", id="jupyterhub-control-panel-link")
assert link, f"Missing jupyterhub-control-panel-link in {page}"
assert link["href"] == url_path_join(app.base_url, "hub/home")

View File

@@ -15,7 +15,7 @@ target_version = [
github_url = "https://github.com/jupyterhub/jupyterhub" github_url = "https://github.com/jupyterhub/jupyterhub"
[tool.tbump.version] [tool.tbump.version]
current = "2.3.0" current = "2.3.1"
# Example of a semver regexp. # Example of a semver regexp.
# Make sure this matches current_version before # Make sure this matches current_version before

File diff suppressed because one or more lines are too long