mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-10 19:43:01 +00:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3d3ad2929c | ||
![]() |
00287ff5ba | ||
![]() |
805d063d1d | ||
![]() |
e6bacf7109 | ||
![]() |
33ccfa7963 | ||
![]() |
593404f558 | ||
![]() |
e7bc282c80 | ||
![]() |
b939b482a1 | ||
![]() |
8afc2c9ae9 | ||
![]() |
d11eda14ed | ||
![]() |
ab79251fe2 | ||
![]() |
484dbf48de | ||
![]() |
6eb526d08a | ||
![]() |
e0a17db5f1 | ||
![]() |
45132b7244 |
@@ -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.2.0
|
version: 2.2.1
|
||||||
servers:
|
servers:
|
||||||
- url: /hub/api
|
- url: /hub/api
|
||||||
security:
|
security:
|
||||||
|
37
docs/source/admin/log-messages.md
Normal file
37
docs/source/admin/log-messages.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Common log messages emitted by JupyterHub
|
||||||
|
|
||||||
|
When debugging errors and outages, looking at the logs emitted by
|
||||||
|
JupyterHub is very helpful. This document tries to document some common
|
||||||
|
log messages, and what they mean.
|
||||||
|
|
||||||
|
## Failing suspected API request to not-running server
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Your logs might be littered with lines that might look slightly scary
|
||||||
|
|
||||||
|
```
|
||||||
|
[W 2022-03-10 17:25:19.774 JupyterHub base:1349] Failing suspected API request to not-running server: /hub/user/<user-name>/api/metrics/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Most likely cause
|
||||||
|
|
||||||
|
This likely means is that the user's server has stopped running but they
|
||||||
|
still have a browser tab open. For example, you might have 3 tabs open, and shut
|
||||||
|
your server down via one. Or you closed your laptop, your server was
|
||||||
|
culled for inactivity, and then you reopen your laptop again! The
|
||||||
|
client side code (JupyterLab, Classic Notebook, etc) does not know
|
||||||
|
yet that the server is dead, and continues to make some API requests.
|
||||||
|
JupyterHub's architecture means that the proxy routes all requests that
|
||||||
|
don't go to a running user server to the hub process itself. The hub
|
||||||
|
process then explicitly returns a failure response, so the client knows
|
||||||
|
that the server is not running anymore. This is used by JupyterLab to
|
||||||
|
tell you your server is not running anymore, and offer you the option
|
||||||
|
to let you restart it.
|
||||||
|
|
||||||
|
Most commonly, you'll see this in reference to the `/api/metrics/v1`
|
||||||
|
URL, used by [jupyter-resource-usage](https://github.com/jupyter-server/jupyter-resource-usage).
|
||||||
|
|
||||||
|
### Actions you can take
|
||||||
|
|
||||||
|
This log message is benign, and there is usually no action for you to take.
|
@@ -8,6 +8,31 @@ command line for details.
|
|||||||
|
|
||||||
## 2.2
|
## 2.2
|
||||||
|
|
||||||
|
### 2.2.1 2021-03-11
|
||||||
|
|
||||||
|
2.2.1 fixes a few small regressions in 2.2.0.
|
||||||
|
|
||||||
|
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/2.2.0...2.2.1))
|
||||||
|
|
||||||
|
#### Bugs fixed
|
||||||
|
|
||||||
|
- Fix clearing cookie with custom xsrf cookie options [#3823](https://github.com/jupyterhub/jupyterhub/pull/3823) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio))
|
||||||
|
- Fix admin dashboard table sorting [#3822](https://github.com/jupyterhub/jupyterhub/pull/3822) ([@NarekA](https://github.com/NarekA), [@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio))
|
||||||
|
|
||||||
|
#### Maintenance and upkeep improvements
|
||||||
|
|
||||||
|
- allow Spawner.server to be mocked without underlying orm_spawner [#3819](https://github.com/jupyterhub/jupyterhub/pull/3819) ([@minrk](https://github.com/minrk), [@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio))
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
- Add some docs on common log messages [#3820](https://github.com/jupyterhub/jupyterhub/pull/3820) ([@yuvipanda](https://github.com/yuvipanda), [@choldgraf](https://github.com/choldgraf), [@consideRatio](https://github.com/consideRatio))
|
||||||
|
|
||||||
|
#### Contributors to this release
|
||||||
|
|
||||||
|
([GitHub contributors page for this release](https://github.com/jupyterhub/jupyterhub/graphs/contributors?from=2022-03-07&to=2022-03-11&type=c))
|
||||||
|
|
||||||
|
[@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Acholdgraf+updated%3A2022-03-07..2022-03-11&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3AconsideRatio+updated%3A2022-03-07..2022-03-11&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Aminrk+updated%3A2022-03-07..2022-03-11&type=Issues) | [@NarekA](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3ANarekA+updated%3A2022-03-07..2022-03-11&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Ayuvipanda+updated%3A2022-03-07..2022-03-11&type=Issues)
|
||||||
|
|
||||||
# 2.2.0 2021-03-07
|
# 2.2.0 2021-03-07
|
||||||
|
|
||||||
JupyterHub 2.2.0 is a small release.
|
JupyterHub 2.2.0 is a small release.
|
||||||
|
@@ -10,4 +10,5 @@ well as other information relevant to running your own JupyterHub over time.
|
|||||||
|
|
||||||
troubleshooting
|
troubleshooting
|
||||||
admin/upgrading
|
admin/upgrading
|
||||||
|
admin/log-messages
|
||||||
changelog
|
changelog
|
||||||
|
@@ -153,10 +153,9 @@ const ServerDashboard = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const EditUserCell = ({ user, numServers, serverName }) => {
|
const EditUserCell = ({ user }) => {
|
||||||
if (serverName) return null;
|
|
||||||
return (
|
return (
|
||||||
<td rowspan={numServers}>
|
<td>
|
||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-xs"
|
className="btn btn-primary btn-xs"
|
||||||
style={{ marginRight: 20 }}
|
style={{ marginRight: 20 }}
|
||||||
@@ -176,6 +175,14 @@ const ServerDashboard = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let servers = user_data.flatMap((user) => {
|
||||||
|
let userServers = Object.values({
|
||||||
|
"": user.server || {},
|
||||||
|
...(user.servers || {}),
|
||||||
|
});
|
||||||
|
return userServers.map((server) => [user, server]);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container" data-testid="container">
|
<div className="container" data-testid="container">
|
||||||
{errorAlert != null ? (
|
{errorAlert != null ? (
|
||||||
@@ -339,87 +346,65 @@ const ServerDashboard = (props) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{user_data.flatMap((e, i) => {
|
{servers.map(([user, server], i) => {
|
||||||
let userServers = Object.values({
|
server.name = server.name || "";
|
||||||
"": e.server,
|
return (
|
||||||
...(e.servers || {}),
|
<tr key={i + "row"} className="user-row">
|
||||||
});
|
<td data-testid="user-row-name">{user.name}</td>
|
||||||
return userServers.map((server) => {
|
<td data-testid="user-row-admin">
|
||||||
server = { name: "", ...server };
|
{user.admin ? "admin" : ""}
|
||||||
return (
|
</td>
|
||||||
<tr key={i + "row"} className="user-row">
|
|
||||||
{!server.name && (
|
|
||||||
<td
|
|
||||||
data-testid="user-row-name"
|
|
||||||
rowspan={userServers.length}
|
|
||||||
>
|
|
||||||
{e.name}
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
{!server.name && (
|
|
||||||
<td
|
|
||||||
data-testid="user-row-admin"
|
|
||||||
rowspan={userServers.length}
|
|
||||||
>
|
|
||||||
{e.admin ? "admin" : ""}
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<td data-testid="user-row-server">
|
<td data-testid="user-row-server">
|
||||||
{server.name ? (
|
{server.name ? (
|
||||||
<p class="text-secondary">{server.name}</p>
|
<p class="text-secondary">{server.name}</p>
|
||||||
) : (
|
) : (
|
||||||
<p style={{ color: "lightgrey" }}>[MAIN]</p>
|
<p style={{ color: "lightgrey" }}>[MAIN]</p>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td data-testid="user-row-last-activity">
|
<td data-testid="user-row-last-activity">
|
||||||
{server.last_activity
|
{server.last_activity
|
||||||
? timeSince(server.last_activity)
|
? timeSince(server.last_activity)
|
||||||
: "Never"}
|
: "Never"}
|
||||||
</td>
|
</td>
|
||||||
<td data-testid="user-row-server-activity">
|
<td data-testid="user-row-server-activity">
|
||||||
{server.started ? (
|
{server.started ? (
|
||||||
// Stop Single-user server
|
// Stop Single-user server
|
||||||
<>
|
<>
|
||||||
<StopServerButton
|
<StopServerButton
|
||||||
serverName={server.name}
|
serverName={server.name}
|
||||||
userName={e.name}
|
userName={user.name}
|
||||||
/>
|
/>
|
||||||
<AccessServerButton
|
<AccessServerButton
|
||||||
serverName={server.name}
|
serverName={server.name}
|
||||||
userName={e.name}
|
userName={user.name}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
// Start Single-user server
|
// Start Single-user server
|
||||||
<>
|
<>
|
||||||
<StartServerButton
|
<StartServerButton
|
||||||
serverName={server.name}
|
serverName={server.name}
|
||||||
userName={e.name}
|
userName={user.name}
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
href={`/spawn/${e.name}${
|
href={`/spawn/${user.name}${
|
||||||
server.name && "/" + server.name
|
server.name && "/" + server.name
|
||||||
}`}
|
}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary btn-xs"
|
||||||
|
style={{ marginRight: 20 }}
|
||||||
>
|
>
|
||||||
<button
|
Spawn Page
|
||||||
className="btn btn-secondary btn-xs"
|
</button>
|
||||||
style={{ marginRight: 20 }}
|
</a>
|
||||||
>
|
</>
|
||||||
Spawn Page
|
)}
|
||||||
</button>
|
</td>
|
||||||
</a>
|
<EditUserCell user={user} />
|
||||||
</>
|
</tr>
|
||||||
)}
|
);
|
||||||
</td>
|
|
||||||
<EditUserCell
|
|
||||||
user={e}
|
|
||||||
numServers={userServers.length}
|
|
||||||
serverName={server.name}
|
|
||||||
/>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@@ -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, 2, 0, "", "")
|
version_info = (2, 2, 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
|
||||||
|
@@ -526,10 +526,16 @@ class BaseHandler(RequestHandler):
|
|||||||
path=url_path_join(self.base_url, 'services'),
|
path=url_path_join(self.base_url, 'services'),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
# clear tornado cookie
|
# clear_cookie only accepts a subset of set_cookie's kwargs
|
||||||
|
clear_xsrf_cookie_kwargs = {
|
||||||
|
key: value
|
||||||
|
for key, value in self.settings.get('xsrf_cookie_kwargs', {})
|
||||||
|
if key in {"path", "domain"}
|
||||||
|
}
|
||||||
|
|
||||||
self.clear_cookie(
|
self.clear_cookie(
|
||||||
'_xsrf',
|
'_xsrf',
|
||||||
**self.settings.get('xsrf_cookie_kwargs', {}),
|
**clear_xsrf_cookie_kwargs,
|
||||||
)
|
)
|
||||||
# Reset _jupyterhub_user
|
# Reset _jupyterhub_user
|
||||||
self._jupyterhub_user = None
|
self._jupyterhub_user = None
|
||||||
|
@@ -195,8 +195,7 @@ class Spawner(LoggingConfigurable):
|
|||||||
# always check that we're in sync with orm_spawner
|
# always check that we're in sync with orm_spawner
|
||||||
if not self.orm_spawner:
|
if not self.orm_spawner:
|
||||||
# no ORM spawner, nothing to check
|
# no ORM spawner, nothing to check
|
||||||
self._server = None
|
return self._server
|
||||||
return None
|
|
||||||
|
|
||||||
orm_server = self.orm_spawner.server
|
orm_server = self.orm_spawner.server
|
||||||
|
|
||||||
@@ -227,6 +226,10 @@ class Spawner(LoggingConfigurable):
|
|||||||
if server.orm_server is None:
|
if server.orm_server is None:
|
||||||
self.log.warning(f"No ORM server for {self._log_name}")
|
self.log.warning(f"No ORM server for {self._log_name}")
|
||||||
self.orm_spawner.server = server.orm_server
|
self.orm_spawner.server = server.orm_server
|
||||||
|
elif server is not None:
|
||||||
|
self.log.warning(
|
||||||
|
"Setting Spawner.server for {self._log_name} with no underlying orm_spawner"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@@ -529,3 +529,10 @@ def test_spawner_server(db):
|
|||||||
spawner.server = None
|
spawner.server = None
|
||||||
db.commit()
|
db.commit()
|
||||||
assert spawner.orm_spawner.server is None
|
assert spawner.orm_spawner.server is None
|
||||||
|
|
||||||
|
# test with no underlying orm.Spawner
|
||||||
|
# (only relevant for mocking, never true for actual Spawners)
|
||||||
|
spawner = Spawner()
|
||||||
|
spawner.server = Server.from_url("http://1.2.3.4")
|
||||||
|
assert spawner.server is not None
|
||||||
|
assert spawner.server.ip == "1.2.3.4"
|
||||||
|
@@ -11,7 +11,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.2.0"
|
current = "2.2.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
Reference in New Issue
Block a user