mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 18:14:10 +00:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2c8b29b6bb | ||
![]() |
a53178a92b | ||
![]() |
e032cda638 | ||
![]() |
40820b3489 | ||
![]() |
80f4454371 | ||
![]() |
4d0005b0b7 | ||
![]() |
86761ff0d4 | ||
![]() |
32a2a3031c | ||
![]() |
16352496da | ||
![]() |
2259f57772 |
41
.github/workflows/release.yml
vendored
41
.github/workflows/release.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Should we push this image to a public registry?
|
- name: Should we push this image to a public registry?
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/master') }}" = "true" ]; then
|
if [ "${{ startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main') }}" = "true" ]; then
|
||||||
# Empty => Docker Hub
|
# Empty => Docker Hub
|
||||||
echo "REGISTRY=" >> $GITHUB_ENV
|
echo "REGISTRY=" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
@@ -89,27 +89,32 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
# Setup docker to build for multiple platforms (requires qemu).
|
# Setup docker to build for multiple platforms, see:
|
||||||
# See:
|
# https://github.com/docker/build-push-action/tree/v2.4.0#usage
|
||||||
# https://github.com/docker/build-push-action/tree/v2.3.0#usage
|
# https://github.com/docker/build-push-action/blob/v2.4.0/docs/advanced/multi-platform.md
|
||||||
# https://github.com/docker/build-push-action/blob/v2.3.0/docs/advanced/multi-platform.md
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU (for docker buildx)
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@25f0500ff22e406f7191a2a8ba8cda16901ca018 # associated tag: v1.0.2
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx (for multi-arch builds)
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@2a4b53665e15ce7d7049afb11ff1f70ff1610609 # associated tag: v1.1.2
|
||||||
with:
|
with:
|
||||||
# Allows pushing to registry on localhost:5000
|
# Allows pushing to registry on localhost:5000
|
||||||
driver-opts: network=host
|
driver-opts: network=host
|
||||||
|
|
||||||
# https://github.com/docker/login-action/tree/v1.8.0#docker-hub
|
- name: Setup push rights to Docker Hub
|
||||||
- name: Login to Docker Hub
|
# This was setup by...
|
||||||
uses: docker/login-action@v1
|
# 1. Creating a Docker Hub service account "jupyterhubbot"
|
||||||
|
# 2. Creating a access token for the service account specific to this
|
||||||
|
# repository: https://hub.docker.com/settings/security
|
||||||
|
# 3. Making the account part of the "bots" team, and granting that team
|
||||||
|
# permissions to push to the relevant images:
|
||||||
|
# https://hub.docker.com/orgs/jupyterhub/teams/bots/permissions
|
||||||
|
# 4. Registering the username and token as a secret for this repo:
|
||||||
|
# https://github.com/jupyterhub/jupyterhub/settings/secrets/actions
|
||||||
if: env.REGISTRY != 'localhost:5000/'
|
if: env.REGISTRY != 'localhost:5000/'
|
||||||
with:
|
run: |
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" -p "${{ secrets.DOCKERHUB_TOKEN }}"
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
# https://github.com/jupyterhub/action-major-minor-tag-calculator
|
# https://github.com/jupyterhub/action-major-minor-tag-calculator
|
||||||
# If this is a tagged build this will return additional parent tags.
|
# If this is a tagged build this will return additional parent tags.
|
||||||
@@ -127,7 +132,7 @@ jobs:
|
|||||||
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub:noref"
|
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub:noref"
|
||||||
|
|
||||||
- name: Build and push jupyterhub
|
- name: Build and push jupyterhub
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@e1b7f96249f2e4c8e4ac1519b9608c0d48944a1f # associated tag: v2.4.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -147,7 +152,7 @@ jobs:
|
|||||||
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub-onbuild:noref"
|
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub-onbuild:noref"
|
||||||
|
|
||||||
- name: Build and push jupyterhub-onbuild
|
- name: Build and push jupyterhub-onbuild
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@e1b7f96249f2e4c8e4ac1519b9608c0d48944a1f # associated tag: v2.4.0
|
||||||
with:
|
with:
|
||||||
build-args: |
|
build-args: |
|
||||||
BASE_IMAGE=${{ fromJson(steps.jupyterhubtags.outputs.tags)[0] }}
|
BASE_IMAGE=${{ fromJson(steps.jupyterhubtags.outputs.tags)[0] }}
|
||||||
@@ -167,7 +172,7 @@ jobs:
|
|||||||
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub-demo:noref"
|
defaultTag: "${{ env.REGISTRY }}jupyterhub/jupyterhub-demo:noref"
|
||||||
|
|
||||||
- name: Build and push jupyterhub-demo
|
- name: Build and push jupyterhub-demo
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@e1b7f96249f2e4c8e4ac1519b9608c0d48944a1f # associated tag: v2.4.0
|
||||||
with:
|
with:
|
||||||
build-args: |
|
build-args: |
|
||||||
BASE_IMAGE=${{ fromJson(steps.onbuildtags.outputs.tags)[0] }}
|
BASE_IMAGE=${{ fromJson(steps.onbuildtags.outputs.tags)[0] }}
|
||||||
|
@@ -1 +1 @@
|
|||||||
Please refer to [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md).
|
Please refer to [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md).
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
Welcome! As a [Jupyter](https://jupyter.org) project,
|
Welcome! As a [Jupyter](https://jupyter.org) project,
|
||||||
you can follow the [Jupyter contributor guide](https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html).
|
you can follow the [Jupyter contributor guide](https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html).
|
||||||
|
|
||||||
Make sure to also follow [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md)
|
Make sure to also follow [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md)
|
||||||
for a friendly and welcoming collaborative environment.
|
for a friendly and welcoming collaborative environment.
|
||||||
|
|
||||||
## Setting up a development environment
|
## Setting up a development environment
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
[](https://github.com/jupyterhub/jupyterhub/actions)
|
[](https://github.com/jupyterhub/jupyterhub/actions)
|
||||||
[](https://hub.docker.com/r/jupyterhub/jupyterhub/tags)
|
[](https://hub.docker.com/r/jupyterhub/jupyterhub/tags)
|
||||||
[](https://circleci.com/gh/jupyterhub/jupyterhub)<!-- CircleCI Token: b5b65862eb2617b9a8d39e79340b0a6b816da8cc -->
|
[](https://circleci.com/gh/jupyterhub/jupyterhub)<!-- CircleCI Token: b5b65862eb2617b9a8d39e79340b0a6b816da8cc -->
|
||||||
[](https://codecov.io/gh/jupyterhub/jupyterhub)
|
[](https://codecov.io/gh/jupyterhub/jupyterhub)
|
||||||
[](https://github.com/jupyterhub/jupyterhub/issues)
|
[](https://github.com/jupyterhub/jupyterhub/issues)
|
||||||
[](https://discourse.jupyter.org/c/jupyterhub)
|
[](https://discourse.jupyter.org/c/jupyterhub)
|
||||||
[](https://gitter.im/jupyterhub/jupyterhub)
|
[](https://gitter.im/jupyterhub/jupyterhub)
|
||||||
@@ -46,7 +46,7 @@ Basic principles for operation are:
|
|||||||
servers.
|
servers.
|
||||||
|
|
||||||
JupyterHub also provides a
|
JupyterHub also provides a
|
||||||
[REST API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyterhub/master/docs/rest-api.yml#/default)
|
[REST API](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyterhub/HEAD/docs/rest-api.yml#/default)
|
||||||
for administration of the Hub and its users.
|
for administration of the Hub and its users.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -239,7 +239,7 @@ our JupyterHub [Gitter](https://gitter.im/jupyterhub/jupyterhub) channel.
|
|||||||
- [Reporting Issues](https://github.com/jupyterhub/jupyterhub/issues)
|
- [Reporting Issues](https://github.com/jupyterhub/jupyterhub/issues)
|
||||||
- [JupyterHub tutorial](https://github.com/jupyterhub/jupyterhub-tutorial)
|
- [JupyterHub tutorial](https://github.com/jupyterhub/jupyterhub-tutorial)
|
||||||
- [Documentation for JupyterHub](https://jupyterhub.readthedocs.io/en/latest/) | [PDF (latest)](https://media.readthedocs.org/pdf/jupyterhub/latest/jupyterhub.pdf) | [PDF (stable)](https://media.readthedocs.org/pdf/jupyterhub/stable/jupyterhub.pdf)
|
- [Documentation for JupyterHub](https://jupyterhub.readthedocs.io/en/latest/) | [PDF (latest)](https://media.readthedocs.org/pdf/jupyterhub/latest/jupyterhub.pdf) | [PDF (stable)](https://media.readthedocs.org/pdf/jupyterhub/stable/jupyterhub.pdf)
|
||||||
- [Documentation for JupyterHub's REST API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyterhub/master/docs/rest-api.yml#/default)
|
- [Documentation for JupyterHub's REST API](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyterhub/HEAD/docs/rest-api.yml#/default)
|
||||||
- [Documentation for Project Jupyter](http://jupyter.readthedocs.io/en/latest/index.html) | [PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)
|
- [Documentation for Project Jupyter](http://jupyter.readthedocs.io/en/latest/index.html) | [PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)
|
||||||
- [Project Jupyter website](https://jupyter.org)
|
- [Project Jupyter website](https://jupyter.org)
|
||||||
- [Project Jupyter community](https://jupyter.org/community)
|
- [Project Jupyter community](https://jupyter.org/community)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# see me at: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/master/docs/rest-api.yml#/default
|
# see me at: https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/HEAD/docs/rest-api.yml#/default
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
info:
|
info:
|
||||||
title: JupyterHub
|
title: JupyterHub
|
||||||
|
@@ -18,7 +18,7 @@ information on:
|
|||||||
- learning more about JupyterHub's API
|
- learning more about JupyterHub's API
|
||||||
|
|
||||||
The same JupyterHub API spec, as found here, is available in an interactive form
|
The same JupyterHub API spec, as found here, is available in an interactive form
|
||||||
`here (on swagger's petstore) <http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/master/docs/rest-api.yml#!/default>`__.
|
`here (on swagger's petstore) <https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/HEAD/docs/rest-api.yml#!/default>`__.
|
||||||
The `OpenAPI Initiative`_ (fka Swagger™) is a project used to describe
|
The `OpenAPI Initiative`_ (fka Swagger™) is a project used to describe
|
||||||
and document RESTful APIs.
|
and document RESTful APIs.
|
||||||
|
|
||||||
|
@@ -24,6 +24,35 @@ This is now also configurable via `JupyterHub.oauth_token_expires_in`.
|
|||||||
The result is that it should be much less likely for auth tokens stored in cookies
|
The result is that it should be much less likely for auth tokens stored in cookies
|
||||||
to expire during the lifetime of a server.
|
to expire during the lifetime of a server.
|
||||||
|
|
||||||
|
### [1.4.1] 2021-05-12
|
||||||
|
|
||||||
|
1.4.1 is a small bugfix release for 1.4.
|
||||||
|
|
||||||
|
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.4.0...1.4.1))
|
||||||
|
|
||||||
|
#### Enhancements made
|
||||||
|
|
||||||
|
#### Bugs fixed
|
||||||
|
|
||||||
|
- define Spawner.delete_forever on base Spawner [#3454](https://github.com/jupyterhub/jupyterhub/pull/3454) ([@minrk](https://github.com/minrk))
|
||||||
|
- patch base handlers from both jupyter_server and notebook [#3437](https://github.com/jupyterhub/jupyterhub/pull/3437) ([@minrk](https://github.com/minrk))
|
||||||
|
|
||||||
|
#### Maintenance and upkeep improvements
|
||||||
|
|
||||||
|
- ci: fix typo in environment variable [#3457](https://github.com/jupyterhub/jupyterhub/pull/3457) ([@consideRatio](https://github.com/consideRatio))
|
||||||
|
- avoid re-using asyncio.Locks across event loops [#3456](https://github.com/jupyterhub/jupyterhub/pull/3456) ([@minrk](https://github.com/minrk))
|
||||||
|
- ci: github workflow security, pin action to sha etc [#3436](https://github.com/jupyterhub/jupyterhub/pull/3436) ([@consideRatio](https://github.com/consideRatio))
|
||||||
|
|
||||||
|
#### Documentation improvements
|
||||||
|
|
||||||
|
- Fix documentation [#3452](https://github.com/jupyterhub/jupyterhub/pull/3452) ([@davidbrochart](https://github.com/davidbrochart))
|
||||||
|
|
||||||
|
#### Contributors to this release
|
||||||
|
|
||||||
|
([GitHub contributors page for this release](https://github.com/jupyterhub/jupyterhub/graphs/contributors?from=2021-04-19&to=2021-05-12&type=c))
|
||||||
|
|
||||||
|
[@0mar](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3A0mar+updated%3A2021-04-19..2021-05-12&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Abetatim+updated%3A2021-04-19..2021-05-12&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3AconsideRatio+updated%3A2021-04-19..2021-05-12&type=Issues) | [@danlester](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Adanlester+updated%3A2021-04-19..2021-05-12&type=Issues) | [@davidbrochart](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Adavidbrochart+updated%3A2021-04-19..2021-05-12&type=Issues) | [@IvanaH8](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3AIvanaH8+updated%3A2021-04-19..2021-05-12&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Amanics+updated%3A2021-04-19..2021-05-12&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Aminrk+updated%3A2021-04-19..2021-05-12&type=Issues) | [@naatebarber](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Anaatebarber+updated%3A2021-04-19..2021-05-12&type=Issues) | [@OrnithOrtion](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3AOrnithOrtion+updated%3A2021-04-19..2021-05-12&type=Issues) | [@support](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Asupport+updated%3A2021-04-19..2021-05-12&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyterhub%2Fjupyterhub+involves%3Awelcome+updated%3A2021-04-19..2021-05-12&type=Issues)
|
||||||
|
|
||||||
### 1.4.0 2021-04-19
|
### 1.4.0 2021-04-19
|
||||||
|
|
||||||
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.3.0...1.4.0))
|
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.3.0...1.4.0))
|
||||||
@@ -1042,7 +1071,8 @@ Fix removal of `/login` page in 0.4.0, breaking some OAuth providers.
|
|||||||
|
|
||||||
First preview release
|
First preview release
|
||||||
|
|
||||||
[unreleased]: https://github.com/jupyterhub/jupyterhub/compare/1.4.0...HEAD
|
[unreleased]: https://github.com/jupyterhub/jupyterhub/compare/1.4.1...HEAD
|
||||||
|
[1.4.1]: https://github.com/jupyterhub/jupyterhub/compare/1.4.0...1.4.1
|
||||||
[1.4.0]: https://github.com/jupyterhub/jupyterhub/compare/1.3.0...1.4.0
|
[1.4.0]: https://github.com/jupyterhub/jupyterhub/compare/1.3.0...1.4.0
|
||||||
[1.3.0]: https://github.com/jupyterhub/jupyterhub/compare/1.2.1...1.3.0
|
[1.3.0]: https://github.com/jupyterhub/jupyterhub/compare/1.2.1...1.3.0
|
||||||
[1.2.2]: https://github.com/jupyterhub/jupyterhub/compare/1.2.1...1.2.2
|
[1.2.2]: https://github.com/jupyterhub/jupyterhub/compare/1.2.1...1.2.2
|
||||||
|
@@ -13,7 +13,7 @@ Building documentation locally
|
|||||||
We use `sphinx <http://sphinx-doc.org>`_ to build our documentation. It takes
|
We use `sphinx <http://sphinx-doc.org>`_ to build our documentation. It takes
|
||||||
our documentation source files (written in `markdown
|
our documentation source files (written in `markdown
|
||||||
<https://daringfireball.net/projects/markdown/>`_ or `reStructuredText
|
<https://daringfireball.net/projects/markdown/>`_ or `reStructuredText
|
||||||
<http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_ &
|
<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_ &
|
||||||
stored under the ``docs/source`` directory) and converts it into various
|
stored under the ``docs/source`` directory) and converts it into various
|
||||||
formats for people to read. To make sure the documentation you write or
|
formats for people to read. To make sure the documentation you write or
|
||||||
change renders correctly, it is good practice to test it locally.
|
change renders correctly, it is good practice to test it locally.
|
||||||
|
@@ -6,8 +6,8 @@ We want you to contribute to JupyterHub in ways that are most exciting
|
|||||||
& useful to you. We value documentation, testing, bug reporting & code equally,
|
& useful to you. We value documentation, testing, bug reporting & code equally,
|
||||||
and are glad to have your contributions in whatever form you wish :)
|
and are glad to have your contributions in whatever form you wish :)
|
||||||
|
|
||||||
Our `Code of Conduct <https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md>`_
|
Our `Code of Conduct <https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md>`_
|
||||||
(`reporting guidelines <https://github.com/jupyter/governance/blob/master/conduct/reporting_online.md>`_)
|
(`reporting guidelines <https://github.com/jupyter/governance/blob/HEAD/conduct/reporting_online.md>`_)
|
||||||
helps keep our community welcoming to as many people as possible.
|
helps keep our community welcoming to as many people as possible.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@@ -30,7 +30,7 @@ Please submit pull requests to update information or to add new institutions or
|
|||||||
|
|
||||||
### University of California Davis
|
### University of California Davis
|
||||||
|
|
||||||
- [Spinning up multiple Jupyter Notebooks on AWS for a tutorial](https://github.com/mblmicdiv/course2017/blob/master/exercises/sourmash-setup.md)
|
- [Spinning up multiple Jupyter Notebooks on AWS for a tutorial](https://github.com/mblmicdiv/course2017/blob/HEAD/exercises/sourmash-setup.md)
|
||||||
|
|
||||||
Although not technically a JupyterHub deployment, this tutorial setup
|
Although not technically a JupyterHub deployment, this tutorial setup
|
||||||
may be helpful to others in the Jupyter community.
|
may be helpful to others in the Jupyter community.
|
||||||
|
@@ -22,7 +22,7 @@ Admin users of JupyterHub, `admin_users`, can add and remove users from
|
|||||||
the user `allowed_users` set. `admin_users` can take actions on other users'
|
the user `allowed_users` set. `admin_users` can take actions on other users'
|
||||||
behalf, such as stopping and restarting their servers.
|
behalf, such as stopping and restarting their servers.
|
||||||
|
|
||||||
A set of initial admin users, `admin_users` can configured be as follows:
|
A set of initial admin users, `admin_users` can be configured as follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
c.Authenticator.admin_users = {'mal', 'zoe'}
|
c.Authenticator.admin_users = {'mal', 'zoe'}
|
||||||
@@ -32,9 +32,9 @@ Users in the admin set are automatically added to the user `allowed_users` set,
|
|||||||
if they are not already present.
|
if they are not already present.
|
||||||
|
|
||||||
Each authenticator may have different ways of determining whether a user is an
|
Each authenticator may have different ways of determining whether a user is an
|
||||||
administrator. By default JupyterHub use the PAMAuthenticator which provide the
|
administrator. By default JupyterHub uses the PAMAuthenticator which provides the
|
||||||
`admin_groups` option and can determine administrator status base on a user
|
`admin_groups` option and can set administrator status based on a user
|
||||||
groups. For example we can let any users in the `wheel` group be admin:
|
group. For example we can let any user in the `wheel` group be admin:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
c.PAMAuthenticator.admin_groups = {'wheel'}
|
c.PAMAuthenticator.admin_groups = {'wheel'}
|
||||||
@@ -42,9 +42,9 @@ c.PAMAuthenticator.admin_groups = {'wheel'}
|
|||||||
|
|
||||||
## Give admin access to other users' notebook servers (`admin_access`)
|
## Give admin access to other users' notebook servers (`admin_access`)
|
||||||
|
|
||||||
Since the default `JupyterHub.admin_access` setting is False, the admins
|
Since the default `JupyterHub.admin_access` setting is `False`, the admins
|
||||||
do not have permission to log in to the single user notebook servers
|
do not have permission to log in to the single user notebook servers
|
||||||
owned by _other users_. If `JupyterHub.admin_access` is set to True,
|
owned by _other users_. If `JupyterHub.admin_access` is set to `True`,
|
||||||
then admins have permission to log in _as other users_ on their
|
then admins have permission to log in _as other users_ on their
|
||||||
respective machines, for debugging. **As a courtesy, you should make
|
respective machines, for debugging. **As a courtesy, you should make
|
||||||
sure your users know if admin_access is enabled.**
|
sure your users know if admin_access is enabled.**
|
||||||
@@ -53,8 +53,8 @@ sure your users know if admin_access is enabled.**
|
|||||||
|
|
||||||
Users can be added to and removed from the Hub via either the admin
|
Users can be added to and removed from the Hub via either the admin
|
||||||
panel or the REST API. When a user is **added**, the user will be
|
panel or the REST API. When a user is **added**, the user will be
|
||||||
automatically added to the allowed users set and database. Restarting the Hub
|
automatically added to the `allowed_users` set and database. Restarting the Hub
|
||||||
will not require manually updating the allowed users set in your config file,
|
will not require manually updating the `allowed_users` set in your config file,
|
||||||
as the users will be loaded from the database.
|
as the users will be loaded from the database.
|
||||||
|
|
||||||
After starting the Hub once, it is not sufficient to **remove** a user
|
After starting the Hub once, it is not sufficient to **remove** a user
|
||||||
@@ -107,8 +107,8 @@ with any provider, is also available.
|
|||||||
|
|
||||||
## Use DummyAuthenticator for testing
|
## Use DummyAuthenticator for testing
|
||||||
|
|
||||||
The :class:`~jupyterhub.auth.DummyAuthenticator` is a simple authenticator that
|
The `DummyAuthenticator` is a simple authenticator that
|
||||||
allows for any username/password unless if a global password has been set. If
|
allows for any username/password unless a global password has been set. If
|
||||||
set, it will allow for any username as long as the correct password is provided.
|
set, it will allow for any username as long as the correct password is provided.
|
||||||
To set a global password, add this to the config file:
|
To set a global password, add this to the config file:
|
||||||
|
|
||||||
|
@@ -44,7 +44,7 @@ jupyterhub -f /etc/jupyterhub/jupyterhub_config.py
|
|||||||
```
|
```
|
||||||
|
|
||||||
The IPython documentation provides additional information on the
|
The IPython documentation provides additional information on the
|
||||||
[config system](http://ipython.readthedocs.io/en/stable/development/config)
|
[config system](http://ipython.readthedocs.io/en/stable/development/config.html)
|
||||||
that Jupyter uses.
|
that Jupyter uses.
|
||||||
|
|
||||||
## Configure using command line options
|
## Configure using command line options
|
||||||
@@ -63,11 +63,11 @@ would enter:
|
|||||||
jupyterhub --ip 10.0.1.2 --port 443 --ssl-key my_ssl.key --ssl-cert my_ssl.cert
|
jupyterhub --ip 10.0.1.2 --port 443 --ssl-key my_ssl.key --ssl-cert my_ssl.cert
|
||||||
```
|
```
|
||||||
|
|
||||||
All configurable options may technically be set on the command-line,
|
All configurable options may technically be set on the command line,
|
||||||
though some are inconvenient to type. To set a particular configuration
|
though some are inconvenient to type. To set a particular configuration
|
||||||
parameter, `c.Class.trait`, you would use the command line option,
|
parameter, `c.Class.trait`, you would use the command line option,
|
||||||
`--Class.trait`, when starting JupyterHub. For example, to configure the
|
`--Class.trait`, when starting JupyterHub. For example, to configure the
|
||||||
`c.Spawner.notebook_dir` trait from the command-line, use the
|
`c.Spawner.notebook_dir` trait from the command line, use the
|
||||||
`--Spawner.notebook_dir` option:
|
`--Spawner.notebook_dir` option:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -89,11 +89,11 @@ meant as illustration, are:
|
|||||||
## Run the proxy separately
|
## Run the proxy separately
|
||||||
|
|
||||||
This is _not_ strictly necessary, but useful in many cases. If you
|
This is _not_ strictly necessary, but useful in many cases. If you
|
||||||
use a custom proxy (e.g. Traefik), this also not needed.
|
use a custom proxy (e.g. Traefik), this is also not needed.
|
||||||
|
|
||||||
Connections to user servers go through the proxy, and _not_ the hub
|
Connections to user servers go through the proxy, and _not_ the hub
|
||||||
itself. If the proxy stays running when the hub restarts (for
|
itself. If the proxy stays running when the hub restarts (for
|
||||||
maintenance, re-configuration, etc.), then use connections are not
|
maintenance, re-configuration, etc.), then user connections are not
|
||||||
interrupted. For simplicity, by default the hub starts the proxy
|
interrupted. For simplicity, by default the hub starts the proxy
|
||||||
automatically, so if the hub restarts, the proxy restarts, and user
|
automatically, so if the hub restarts, the proxy restarts, and user
|
||||||
connections are interrupted. It is easy to run the proxy separately,
|
connections are interrupted. It is easy to run the proxy separately,
|
||||||
|
@@ -26,7 +26,7 @@ so Breq would open `/user/breq/notebooks/foo.ipynb` and
|
|||||||
Seivarden would open `/user/seivarden/notebooks/foo.ipynb`, etc.
|
Seivarden would open `/user/seivarden/notebooks/foo.ipynb`, etc.
|
||||||
|
|
||||||
JupyterHub has a special URL that does exactly this!
|
JupyterHub has a special URL that does exactly this!
|
||||||
It's called `/hub/user-redirect/...` and after the visitor logs in,
|
It's called `/hub/user-redirect/...`.
|
||||||
So if you replace `/user/yourname` in your URL bar
|
So if you replace `/user/yourname` in your URL bar
|
||||||
with `/hub/user-redirect` any visitor should get the same
|
with `/hub/user-redirect` any visitor should get the same
|
||||||
URL on their own server, rather than visiting yours.
|
URL on their own server, rather than visiting yours.
|
||||||
|
@@ -11,7 +11,7 @@ Yes! JupyterHub has been used at-scale for large pools of users, as well
|
|||||||
as complex and high-performance computing. For example, UC Berkeley uses
|
as complex and high-performance computing. For example, UC Berkeley uses
|
||||||
JupyterHub for its Data Science Education Program courses (serving over
|
JupyterHub for its Data Science Education Program courses (serving over
|
||||||
3,000 students). The Pangeo project uses JupyterHub to provide access
|
3,000 students). The Pangeo project uses JupyterHub to provide access
|
||||||
to scalable cloud computing with Dask. JupyterHub is stable customizable
|
to scalable cloud computing with Dask. JupyterHub is stable and customizable
|
||||||
to the use-cases of large organizations.
|
to the use-cases of large organizations.
|
||||||
|
|
||||||
### I keep hearing about Jupyter Notebook, JupyterLab, and now JupyterHub. What’s the difference?
|
### I keep hearing about Jupyter Notebook, JupyterLab, and now JupyterHub. What’s the difference?
|
||||||
@@ -27,14 +27,14 @@ Here is a quick breakdown of these three tools:
|
|||||||
for other parts of the data science stack.
|
for other parts of the data science stack.
|
||||||
- **JupyterHub** is an application that manages interactive computing sessions for **multiple users**.
|
- **JupyterHub** is an application that manages interactive computing sessions for **multiple users**.
|
||||||
It also connects them with infrastructure those users wish to access. It can provide
|
It also connects them with infrastructure those users wish to access. It can provide
|
||||||
remote access to Jupyter Notebooks and Jupyter Lab for many people.
|
remote access to Jupyter Notebooks and JupyterLab for many people.
|
||||||
|
|
||||||
## For management
|
## For management
|
||||||
|
|
||||||
### Briefly, what problem does JupyterHub solve for us?
|
### Briefly, what problem does JupyterHub solve for us?
|
||||||
|
|
||||||
JupyterHub provides a shared platform for data science and collaboration.
|
JupyterHub provides a shared platform for data science and collaboration.
|
||||||
It allows users to utilize familiar data science workflows (such as the scientific python stack,
|
It allows users to utilize familiar data science workflows (such as the scientific Python stack,
|
||||||
the R tidyverse, and Jupyter Notebooks) on institutional infrastructure. It also allows administrators
|
the R tidyverse, and Jupyter Notebooks) on institutional infrastructure. It also allows administrators
|
||||||
some control over access to resources, security, environments, and authentication.
|
some control over access to resources, security, environments, and authentication.
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ industry, and government research labs. It is most-commonly used by two kinds of
|
|||||||
- Large teams (e.g., a department, a large class, or a large group of remote users) to provide
|
- Large teams (e.g., a department, a large class, or a large group of remote users) to provide
|
||||||
access to organizational hardware, data, and analytics environments at scale.
|
access to organizational hardware, data, and analytics environments at scale.
|
||||||
|
|
||||||
Here are a sample of organizations that use JupyterHub:
|
Here is a sample of organizations that use JupyterHub:
|
||||||
|
|
||||||
- **Universities and colleges**: UC Berkeley, UC San Diego, Cal Poly SLO, Harvard University, University of Chicago,
|
- **Universities and colleges**: UC Berkeley, UC San Diego, Cal Poly SLO, Harvard University, University of Chicago,
|
||||||
University of Oslo, University of Sheffield, Université Paris Sud, University of Versailles
|
University of Oslo, University of Sheffield, Université Paris Sud, University of Versailles
|
||||||
@@ -99,7 +99,7 @@ that we currently suggest are:
|
|||||||
guide that runs on Kubernetes. Better for larger or dynamic user groups (50-10,000) or more complex
|
guide that runs on Kubernetes. Better for larger or dynamic user groups (50-10,000) or more complex
|
||||||
compute/data needs.
|
compute/data needs.
|
||||||
- [The Littlest JupyterHub](https://tljh.jupyter.org) is a lightweight JupyterHub that runs on a single
|
- [The Littlest JupyterHub](https://tljh.jupyter.org) is a lightweight JupyterHub that runs on a single
|
||||||
single machine (in the cloud or under your desk). Better for smaller usergroups (4-80) or more
|
single machine (in the cloud or under your desk). Better for smaller user groups (4-80) or more
|
||||||
lightweight computational resources.
|
lightweight computational resources.
|
||||||
|
|
||||||
### Does JupyterHub run well in the cloud?
|
### Does JupyterHub run well in the cloud?
|
||||||
@@ -123,7 +123,7 @@ level for several years, and makes a number of "default" security decisions that
|
|||||||
users.
|
users.
|
||||||
|
|
||||||
- For security considerations in the base JupyterHub application,
|
- For security considerations in the base JupyterHub application,
|
||||||
[see the JupyterHub security page](https://jupyterhub.readthedocs.io/en/stable/reference/websecurity.html)
|
[see the JupyterHub security page](https://jupyterhub.readthedocs.io/en/stable/reference/websecurity.html).
|
||||||
- For security considerations when deploying JupyterHub on Kubernetes, see the
|
- For security considerations when deploying JupyterHub on Kubernetes, see the
|
||||||
[JupyterHub on Kubernetes security page](https://zero-to-jupyterhub.readthedocs.io/en/latest/security.html).
|
[JupyterHub on Kubernetes security page](https://zero-to-jupyterhub.readthedocs.io/en/latest/security.html).
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ how those resources are controlled is taken care of by the non-JupyterHub applic
|
|||||||
|
|
||||||
Yes - JupyterHub can provide access to many kinds of computing infrastructure.
|
Yes - JupyterHub can provide access to many kinds of computing infrastructure.
|
||||||
Especially when combined with other open-source schedulers such as Dask, you can manage fairly
|
Especially when combined with other open-source schedulers such as Dask, you can manage fairly
|
||||||
complex computing infrastructure from the interactive sessions of a JupyterHub. For example
|
complex computing infrastructures from the interactive sessions of a JupyterHub. For example
|
||||||
[see the Dask HPC page](https://docs.dask.org/en/latest/setup/hpc.html).
|
[see the Dask HPC page](https://docs.dask.org/en/latest/setup/hpc.html).
|
||||||
|
|
||||||
### How much resources do user sessions take?
|
### How much resources do user sessions take?
|
||||||
@@ -192,7 +192,7 @@ This is highly configurable by the administrator. If you wish for your users to
|
|||||||
data analytics environments for prototyping and light data exploring, you can restrict their
|
data analytics environments for prototyping and light data exploring, you can restrict their
|
||||||
memory and CPU based on the resources that you have available. If you'd like your JupyterHub
|
memory and CPU based on the resources that you have available. If you'd like your JupyterHub
|
||||||
to serve as a gateway to high-performance compute or data resources, you may increase the
|
to serve as a gateway to high-performance compute or data resources, you may increase the
|
||||||
resources available on user machines, or connect them with computing infrastructure elsewhere.
|
resources available on user machines, or connect them with computing infrastructures elsewhere.
|
||||||
|
|
||||||
### Can I customize the look and feel of a JupyterHub?
|
### Can I customize the look and feel of a JupyterHub?
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ your JupyterHub with the various services and tools that you wish to provide to
|
|||||||
### How well does JupyterHub scale? What are JupyterHub's limitations?
|
### How well does JupyterHub scale? What are JupyterHub's limitations?
|
||||||
|
|
||||||
JupyterHub works well at both a small scale (e.g., a single VM or machine) as well as a
|
JupyterHub works well at both a small scale (e.g., a single VM or machine) as well as a
|
||||||
high scale (e.g., a scalable Kubernetes cluster). It can be used for teams as small a 2, and
|
high scale (e.g., a scalable Kubernetes cluster). It can be used for teams as small as 2, and
|
||||||
for user bases as large as 10,000. The scalability of JupyterHub largely depends on the
|
for user bases as large as 10,000. The scalability of JupyterHub largely depends on the
|
||||||
infrastructure on which it is deployed. JupyterHub has been designed to be lightweight and
|
infrastructure on which it is deployed. JupyterHub has been designed to be lightweight and
|
||||||
flexible, so you can tailor your JupyterHub deployment to your needs.
|
flexible, so you can tailor your JupyterHub deployment to your needs.
|
||||||
@@ -249,7 +249,7 @@ share their results with one another.
|
|||||||
|
|
||||||
JupyterHub also provides a computational framework to share computational narratives between
|
JupyterHub also provides a computational framework to share computational narratives between
|
||||||
different levels of an organization. For example, data scientists can share Jupyter Notebooks
|
different levels of an organization. For example, data scientists can share Jupyter Notebooks
|
||||||
rendered as [voila dashboards](https://voila.readthedocs.io/en/stable/) with those who are not
|
rendered as [Voilà dashboards](https://voila.readthedocs.io/en/stable/) with those who are not
|
||||||
familiar with programming, or create publicly-available interactive analyses to allow others to
|
familiar with programming, or create publicly-available interactive analyses to allow others to
|
||||||
interact with your work.
|
interact with your work.
|
||||||
|
|
||||||
|
@@ -43,7 +43,7 @@ port.
|
|||||||
|
|
||||||
By default, this REST API listens on port 8001 of `localhost` only.
|
By default, this REST API listens on port 8001 of `localhost` only.
|
||||||
The Hub service talks to the proxy via a REST API on a secondary port. The
|
The Hub service talks to the proxy via a REST API on a secondary port. The
|
||||||
API URL can be configured separately and override the default settings.
|
API URL can be configured separately to override the default settings.
|
||||||
|
|
||||||
### Set api_url
|
### Set api_url
|
||||||
|
|
||||||
@@ -82,13 +82,13 @@ c.JupyterHub.hub_ip = '10.0.1.4'
|
|||||||
c.JupyterHub.hub_port = 54321
|
c.JupyterHub.hub_port = 54321
|
||||||
```
|
```
|
||||||
|
|
||||||
**Added in 0.8:** The `c.JupyterHub.hub_connect_ip` setting is the ip address or
|
**Added in 0.8:** The `c.JupyterHub.hub_connect_ip` setting is the IP address or
|
||||||
hostname that other services should use to connect to the Hub. A common
|
hostname that other services should use to connect to the Hub. A common
|
||||||
configuration for, e.g. docker, is:
|
configuration for, e.g. docker, is:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
c.JupyterHub.hub_ip = '0.0.0.0' # listen on all interfaces
|
c.JupyterHub.hub_ip = '0.0.0.0' # listen on all interfaces
|
||||||
c.JupyterHub.hub_connect_ip = '10.0.1.4' # ip as seen on the docker network. Can also be a hostname.
|
c.JupyterHub.hub_connect_ip = '10.0.1.4' # IP as seen on the docker network. Can also be a hostname.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adjusting the hub's URL
|
## Adjusting the hub's URL
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
When working with JupyterHub, a **Service** is defined as a process
|
When working with JupyterHub, a **Service** is defined as a process
|
||||||
that interacts with the Hub's REST API. A Service may perform a specific
|
that interacts with the Hub's REST API. A Service may perform a specific
|
||||||
or action or task. For example, shutting down individuals' single user
|
action or task. For example, shutting down individuals' single user
|
||||||
notebook servers that have been idle for some time is a good example of
|
notebook servers that have been idle for some time is a good example of
|
||||||
a task that could be automated by a Service. Let's look at how the
|
a task that could be automated by a Service. Let's look at how the
|
||||||
[jupyterhub_idle_culler][] script can be used as a Service.
|
[jupyterhub_idle_culler][] script can be used as a Service.
|
||||||
@@ -114,7 +114,7 @@ interact with it.
|
|||||||
This will run the idle culler service manually. It can be run as a standalone
|
This will run the idle culler service manually. It can be run as a standalone
|
||||||
script anywhere with access to the Hub, and will periodically check for idle
|
script anywhere with access to the Hub, and will periodically check for idle
|
||||||
servers and shut them down via the Hub's REST API. In order to shutdown the
|
servers and shut them down via the Hub's REST API. In order to shutdown the
|
||||||
servers, the token given to cull-idle must have admin privileges.
|
servers, the token given to `cull-idle` must have admin privileges.
|
||||||
|
|
||||||
Generate an API token and store it in the `JUPYTERHUB_API_TOKEN` environment
|
Generate an API token and store it in the `JUPYTERHUB_API_TOKEN` environment
|
||||||
variable. Run `jupyterhub_idle_culler` manually.
|
variable. Run `jupyterhub_idle_culler` manually.
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
# Spawners and single-user notebook servers
|
# Spawners and single-user notebook servers
|
||||||
|
|
||||||
Since the single-user server is an instance of `jupyter notebook`, an entire separate
|
Since the single-user server is an instance of `jupyter notebook`, an entire separate
|
||||||
multi-process application, there are many aspect of that server can configure, and a lot of ways
|
multi-process application, there are many aspects of that server that can be configured, and a lot
|
||||||
to express that configuration.
|
of ways to express that configuration.
|
||||||
|
|
||||||
At the JupyterHub level, you can set some values on the Spawner. The simplest of these is
|
At the JupyterHub level, you can set some values on the Spawner. The simplest of these is
|
||||||
`Spawner.notebook_dir`, which lets you set the root directory for a user's server. This root
|
`Spawner.notebook_dir`, which lets you set the root directory for a user's server. This root
|
||||||
@@ -14,7 +14,7 @@ expanded to the user's home directory.
|
|||||||
c.Spawner.notebook_dir = '~/notebooks'
|
c.Spawner.notebook_dir = '~/notebooks'
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also specify extra command-line arguments to the notebook server with:
|
You can also specify extra command line arguments to the notebook server with:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
c.Spawner.args = ['--debug', '--profile=PHYS131']
|
c.Spawner.args = ['--debug', '--profile=PHYS131']
|
||||||
|
@@ -115,8 +115,8 @@ We want you to contribute to JupyterHub in ways that are most exciting
|
|||||||
& useful to you. We value documentation, testing, bug reporting & code equally,
|
& useful to you. We value documentation, testing, bug reporting & code equally,
|
||||||
and are glad to have your contributions in whatever form you wish :)
|
and are glad to have your contributions in whatever form you wish :)
|
||||||
|
|
||||||
Our `Code of Conduct <https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md>`_
|
Our `Code of Conduct <https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md>`_
|
||||||
(`reporting guidelines <https://github.com/jupyter/governance/blob/master/conduct/reporting_online.md>`_)
|
(`reporting guidelines <https://github.com/jupyter/governance/blob/HEAD/conduct/reporting_online.md>`_)
|
||||||
helps keep our community welcoming to as many people as possible.
|
helps keep our community welcoming to as many people as possible.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
@@ -147,4 +147,4 @@ Questions? Suggestions?
|
|||||||
|
|
||||||
.. _JupyterHub: https://github.com/jupyterhub/jupyterhub
|
.. _JupyterHub: https://github.com/jupyterhub/jupyterhub
|
||||||
.. _Jupyter notebook: https://jupyter-notebook.readthedocs.io/en/latest/
|
.. _Jupyter notebook: https://jupyter-notebook.readthedocs.io/en/latest/
|
||||||
.. _REST API: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/master/docs/rest-api.yml#!/default
|
.. _REST API: https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/HEAD/docs/rest-api.yml#!/default
|
||||||
|
@@ -3,4 +3,4 @@
|
|||||||
JupyterHub the hard way
|
JupyterHub the hard way
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
This guide has moved to https://github.com/jupyterhub/jupyterhub-the-hard-way/blob/master/docs/installation-guide-hard.md
|
This guide has moved to https://github.com/jupyterhub/jupyterhub-the-hard-way/blob/HEAD/docs/installation-guide-hard.md
|
||||||
|
@@ -259,7 +259,7 @@ PAM session.
|
|||||||
|
|
||||||
Beginning with version 0.8, JupyterHub is an OAuth provider.
|
Beginning with version 0.8, JupyterHub is an OAuth provider.
|
||||||
|
|
||||||
[authenticator]: https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/auth.py
|
[authenticator]: https://github.com/jupyterhub/jupyterhub/blob/HEAD/jupyterhub/auth.py
|
||||||
[pam]: https://en.wikipedia.org/wiki/Pluggable_authentication_module
|
[pam]: https://en.wikipedia.org/wiki/Pluggable_authentication_module
|
||||||
[oauth]: https://en.wikipedia.org/wiki/OAuth
|
[oauth]: https://en.wikipedia.org/wiki/OAuth
|
||||||
[github oauth]: https://developer.github.com/v3/oauth/
|
[github oauth]: https://developer.github.com/v3/oauth/
|
||||||
|
@@ -209,7 +209,7 @@ be viewed in a more [interactive style on swagger's petstore][].
|
|||||||
Both resources contain the same information and differ only in its display.
|
Both resources contain the same information and differ only in its display.
|
||||||
Note: The Swagger specification is being renamed the [OpenAPI Initiative][].
|
Note: The Swagger specification is being renamed the [OpenAPI Initiative][].
|
||||||
|
|
||||||
[interactive style on swagger's petstore]: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/master/docs/rest-api.yml#!/default
|
[interactive style on swagger's petstore]: https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/HEAD/docs/rest-api.yml#!/default
|
||||||
[openapi initiative]: https://www.openapis.org/
|
[openapi initiative]: https://www.openapis.org/
|
||||||
[jupyterhub rest api]: ./rest-api
|
[jupyterhub rest api]: ./rest-api
|
||||||
[jupyter notebook rest api]: http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml
|
[jupyter notebook rest api]: https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/HEAD/notebook/services/api/api.yaml
|
||||||
|
@@ -230,7 +230,7 @@ configurable by the `cookie_cache_max_age` setting (default: five minutes).
|
|||||||
For example, you have a Flask service that returns information about a user.
|
For example, you have a Flask service that returns information about a user.
|
||||||
JupyterHub's HubAuth class can be used to authenticate requests to the Flask
|
JupyterHub's HubAuth class can be used to authenticate requests to the Flask
|
||||||
service. See the `service-whoami-flask` example in the
|
service. See the `service-whoami-flask` example in the
|
||||||
[JupyterHub GitHub repo](https://github.com/jupyterhub/jupyterhub/tree/master/examples/service-whoami-flask)
|
[JupyterHub GitHub repo](https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-whoami-flask)
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@@ -125,7 +125,7 @@ If the `Spawner.options_form` is defined, when a user tries to start their serve
|
|||||||
|
|
||||||
If `Spawner.options_form` is undefined, the user's server is spawned directly, and no spawn page is rendered.
|
If `Spawner.options_form` is undefined, the user's server is spawned directly, and no spawn page is rendered.
|
||||||
|
|
||||||
See [this example](https://github.com/jupyterhub/jupyterhub/blob/master/examples/spawn-form/jupyterhub_config.py) for a form that allows custom CLI args for the local spawner.
|
See [this example](https://github.com/jupyterhub/jupyterhub/blob/HEAD/examples/spawn-form/jupyterhub_config.py) for a form that allows custom CLI args for the local spawner.
|
||||||
|
|
||||||
### `Spawner.options_from_form`
|
### `Spawner.options_from_form`
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ which would return:
|
|||||||
|
|
||||||
When `Spawner.start` is called, this dictionary is accessible as `self.user_options`.
|
When `Spawner.start` is called, this dictionary is accessible as `self.user_options`.
|
||||||
|
|
||||||
[spawner]: https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/spawner.py
|
[spawner]: https://github.com/jupyterhub/jupyterhub/blob/HEAD/jupyterhub/spawner.py
|
||||||
|
|
||||||
## Writing a custom spawner
|
## Writing a custom spawner
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ appearance.
|
|||||||
|
|
||||||
JupyterHub will look for custom templates in all of the paths in the
|
JupyterHub will look for custom templates in all of the paths in the
|
||||||
`JupyterHub.template_paths` configuration option, falling back on the
|
`JupyterHub.template_paths` configuration option, falling back on the
|
||||||
[default templates](https://github.com/jupyterhub/jupyterhub/tree/master/share/jupyterhub/templates)
|
[default templates](https://github.com/jupyterhub/jupyterhub/tree/HEAD/share/jupyterhub/templates)
|
||||||
if no custom template with that name is found. This fallback
|
if no custom template with that name is found. This fallback
|
||||||
behavior is new in version 0.9; previous versions searched only those paths
|
behavior is new in version 0.9; previous versions searched only those paths
|
||||||
explicitly included in `template_paths`. You may override as many
|
explicitly included in `template_paths`. You may override as many
|
||||||
@@ -21,7 +21,7 @@ or as few templates as you desire.
|
|||||||
Jinja provides a mechanism to [extend templates](http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance).
|
Jinja provides a mechanism to [extend templates](http://jinja.pocoo.org/docs/2.10/templates/#template-inheritance).
|
||||||
A base template can define a `block`, and child templates can replace or
|
A base template can define a `block`, and child templates can replace or
|
||||||
supplement the material in the block. The
|
supplement the material in the block. The
|
||||||
[JupyterHub templates](https://github.com/jupyterhub/jupyterhub/tree/master/share/jupyterhub/templates)
|
[JupyterHub templates](https://github.com/jupyterhub/jupyterhub/tree/HEAD/share/jupyterhub/templates)
|
||||||
make extensive use of blocks, which allows you to customize parts of the
|
make extensive use of blocks, which allows you to customize parts of the
|
||||||
interface easily.
|
interface easily.
|
||||||
|
|
||||||
|
@@ -234,7 +234,7 @@ With a docker container, pass in the environment variable with the run command:
|
|||||||
-e JUPYTERHUB_API_TOKEN=my_secret_token \
|
-e JUPYTERHUB_API_TOKEN=my_secret_token \
|
||||||
jupyter/datascience-notebook:latest
|
jupyter/datascience-notebook:latest
|
||||||
|
|
||||||
[This example](https://github.com/jupyterhub/jupyterhub/tree/master/examples/service-notebook/external) demonstrates how to combine the use of the `jupyterhub-singleuser` environment variables when launching a Notebook as an externally managed service.
|
[This example](https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-notebook/external) demonstrates how to combine the use of the `jupyterhub-singleuser` environment variables when launching a Notebook as an externally managed service.
|
||||||
|
|
||||||
## How do I...?
|
## How do I...?
|
||||||
|
|
||||||
|
@@ -148,9 +148,9 @@ else
|
|||||||
echo "...initial content loading for user ..."
|
echo "...initial content loading for user ..."
|
||||||
mkdir $USER_DIRECTORY/tutorials
|
mkdir $USER_DIRECTORY/tutorials
|
||||||
cd $USER_DIRECTORY/tutorials
|
cd $USER_DIRECTORY/tutorials
|
||||||
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/master.zip
|
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/HEAD.zip
|
||||||
unzip -o master.zip
|
unzip -o HEAD.zip
|
||||||
rm master.zip
|
rm HEAD.zip
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@@ -40,9 +40,9 @@ else
|
|||||||
echo "...initial content loading for user ..."
|
echo "...initial content loading for user ..."
|
||||||
mkdir $USER_DIRECTORY/tutorials
|
mkdir $USER_DIRECTORY/tutorials
|
||||||
cd $USER_DIRECTORY/tutorials
|
cd $USER_DIRECTORY/tutorials
|
||||||
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/master.zip
|
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/HEAD.zip
|
||||||
unzip -o master.zip
|
unzip -o HEAD.zip
|
||||||
rm master.zip
|
rm HEAD.zip
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
version_info = (
|
version_info = (
|
||||||
1,
|
1,
|
||||||
4,
|
4,
|
||||||
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
|
||||||
)
|
)
|
||||||
|
@@ -238,11 +238,7 @@ class UserAPIHandler(APIHandler):
|
|||||||
|
|
||||||
await maybe_future(self.authenticator.delete_user(user))
|
await maybe_future(self.authenticator.delete_user(user))
|
||||||
|
|
||||||
# allow the spawner to cleanup any persistent resources associated with the user
|
await user.delete_spawners()
|
||||||
try:
|
|
||||||
await user.spawner.delete_forever()
|
|
||||||
except Exception as e:
|
|
||||||
self.log.error("Error cleaning up persistent resources: %s" % e)
|
|
||||||
|
|
||||||
# remove from registry
|
# remove from registry
|
||||||
self.users.delete(user)
|
self.users.delete(user)
|
||||||
@@ -484,10 +480,18 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
options = self.get_json_body()
|
options = self.get_json_body()
|
||||||
remove = (options or {}).get('remove', False)
|
remove = (options or {}).get('remove', False)
|
||||||
|
|
||||||
def _remove_spawner(f=None):
|
async def _remove_spawner(f=None):
|
||||||
if f and f.exception():
|
"""Remove the spawner object
|
||||||
return
|
|
||||||
|
only called after it stops successfully
|
||||||
|
"""
|
||||||
|
if f:
|
||||||
|
# await f, stop on error,
|
||||||
|
# leaving resources in the db in case of failure to stop
|
||||||
|
await f
|
||||||
self.log.info("Deleting spawner %s", spawner._log_name)
|
self.log.info("Deleting spawner %s", spawner._log_name)
|
||||||
|
await maybe_future(user._delete_spawner(spawner))
|
||||||
|
|
||||||
self.db.delete(spawner.orm_spawner)
|
self.db.delete(spawner.orm_spawner)
|
||||||
user.spawners.pop(server_name, None)
|
user.spawners.pop(server_name, None)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
@@ -508,7 +512,8 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
self.set_header('Content-Type', 'text/plain')
|
self.set_header('Content-Type', 'text/plain')
|
||||||
self.set_status(202)
|
self.set_status(202)
|
||||||
if remove:
|
if remove:
|
||||||
spawner._stop_future.add_done_callback(_remove_spawner)
|
# schedule remove when stop completes
|
||||||
|
asyncio.ensure_future(_remove_spawner(spawner._stop_future))
|
||||||
return
|
return
|
||||||
|
|
||||||
if spawner.pending:
|
if spawner.pending:
|
||||||
@@ -526,9 +531,10 @@ class UserServerAPIHandler(APIHandler):
|
|||||||
|
|
||||||
if remove:
|
if remove:
|
||||||
if stop_future:
|
if stop_future:
|
||||||
stop_future.add_done_callback(_remove_spawner)
|
# schedule remove when stop completes
|
||||||
|
asyncio.ensure_future(_remove_spawner(spawner._stop_future))
|
||||||
else:
|
else:
|
||||||
_remove_spawner()
|
await _remove_spawner()
|
||||||
|
|
||||||
status = 202 if spawner._stop_pending else 204
|
status = 202 if spawner._stop_pending else 204
|
||||||
self.set_header('Content-Type', 'text/plain')
|
self.set_header('Content-Type', 'text/plain')
|
||||||
|
@@ -24,6 +24,7 @@ import time
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
from weakref import WeakKeyDictionary
|
||||||
|
|
||||||
from tornado.httpclient import AsyncHTTPClient
|
from tornado.httpclient import AsyncHTTPClient
|
||||||
from tornado.httpclient import HTTPError
|
from tornado.httpclient import HTTPError
|
||||||
@@ -44,7 +45,6 @@ from .metrics import CHECK_ROUTES_DURATION_SECONDS
|
|||||||
from .metrics import PROXY_POLL_DURATION_SECONDS
|
from .metrics import PROXY_POLL_DURATION_SECONDS
|
||||||
from .objects import Server
|
from .objects import Server
|
||||||
from .utils import exponential_backoff
|
from .utils import exponential_backoff
|
||||||
from .utils import make_ssl_context
|
|
||||||
from .utils import url_path_join
|
from .utils import url_path_join
|
||||||
from jupyterhub.traitlets import Command
|
from jupyterhub.traitlets import Command
|
||||||
|
|
||||||
@@ -55,11 +55,18 @@ def _one_at_a_time(method):
|
|||||||
If multiple concurrent calls to this method are made,
|
If multiple concurrent calls to this method are made,
|
||||||
queue them instead of allowing them to be concurrently outstanding.
|
queue them instead of allowing them to be concurrently outstanding.
|
||||||
"""
|
"""
|
||||||
method._lock = asyncio.Lock()
|
# use weak dict for locks
|
||||||
|
# so that the lock is always acquired within the current asyncio loop
|
||||||
|
# should only be relevant in testing, where eventloops are created and destroyed often
|
||||||
|
method._locks = WeakKeyDictionary()
|
||||||
|
|
||||||
@wraps(method)
|
@wraps(method)
|
||||||
async def locked_method(*args, **kwargs):
|
async def locked_method(*args, **kwargs):
|
||||||
async with method._lock:
|
loop = asyncio.get_event_loop()
|
||||||
|
lock = method._locks.get(loop, None)
|
||||||
|
if lock is None:
|
||||||
|
lock = method._locks[loop] = asyncio.Lock()
|
||||||
|
async with lock:
|
||||||
return await method(*args, **kwargs)
|
return await method(*args, **kwargs)
|
||||||
|
|
||||||
return locked_method
|
return locked_method
|
||||||
|
@@ -10,9 +10,11 @@ with JupyterHub authentication mixins enabled.
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
@@ -99,19 +101,26 @@ class JupyterHubLoginHandlerMixin:
|
|||||||
Thus shouldn't be called anymore because HubAuthenticatedHandler
|
Thus shouldn't be called anymore because HubAuthenticatedHandler
|
||||||
should have already overridden get_current_user().
|
should have already overridden get_current_user().
|
||||||
|
|
||||||
Keep here to prevent unlikely circumstance from losing auth.
|
Keep here to protect uncommon circumstance of multiple BaseHandlers
|
||||||
|
from missing auth.
|
||||||
|
|
||||||
|
e.g. when multiple BaseHandler classes are used.
|
||||||
"""
|
"""
|
||||||
if HubAuthenticatedHandler not in handler.__class__.mro():
|
if HubAuthenticatedHandler not in handler.__class__.mro():
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"Expected to see HubAuthenticatedHandler in {handler.__class__}.mro()",
|
f"Expected to see HubAuthenticatedHandler in {handler.__class__}.mro(),"
|
||||||
|
" patching in at call time. Hub authentication is still applied.",
|
||||||
RuntimeWarning,
|
RuntimeWarning,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
|
# patch HubAuthenticated into the instance
|
||||||
handler.__class__ = type(
|
handler.__class__ = type(
|
||||||
handler.__class__.__name__,
|
handler.__class__.__name__,
|
||||||
(HubAuthenticatedHandler, handler.__class__),
|
(HubAuthenticatedHandler, handler.__class__),
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
# patch into the class itself so this doesn't happen again for the same class
|
||||||
|
patch_base_handler(handler.__class__)
|
||||||
return handler.get_current_user()
|
return handler.get_current_user()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -683,6 +692,97 @@ def detect_base_package(App):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _nice_cls_repr(cls):
|
||||||
|
"""Nice repr of classes, e.g. 'module.submod.Class'
|
||||||
|
|
||||||
|
Also accepts tuples of classes
|
||||||
|
"""
|
||||||
|
return f"{cls.__module__}.{cls.__name__}"
|
||||||
|
|
||||||
|
|
||||||
|
def patch_base_handler(BaseHandler, log=None):
|
||||||
|
"""Patch HubAuthenticated into a base handler class
|
||||||
|
|
||||||
|
so anything inheriting from BaseHandler uses Hub authentication.
|
||||||
|
This works *even after* subclasses have imported and inherited from BaseHandler.
|
||||||
|
|
||||||
|
.. versionadded: 1.5
|
||||||
|
Made available as an importable utility
|
||||||
|
"""
|
||||||
|
if log is None:
|
||||||
|
log = logging.getLogger()
|
||||||
|
|
||||||
|
if HubAuthenticatedHandler not in BaseHandler.__bases__:
|
||||||
|
new_bases = (HubAuthenticatedHandler,) + BaseHandler.__bases__
|
||||||
|
log.info(
|
||||||
|
"Patching auth into {mod}.{name}({old_bases}) -> {name}({new_bases})".format(
|
||||||
|
mod=BaseHandler.__module__,
|
||||||
|
name=BaseHandler.__name__,
|
||||||
|
old_bases=', '.join(
|
||||||
|
_nice_cls_repr(cls) for cls in BaseHandler.__bases__
|
||||||
|
),
|
||||||
|
new_bases=', '.join(_nice_cls_repr(cls) for cls in new_bases),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
BaseHandler.__bases__ = new_bases
|
||||||
|
# We've now inserted our class as a parent of BaseHandler,
|
||||||
|
# but we also need to ensure BaseHandler *itself* doesn't
|
||||||
|
# override the public tornado API methods we have inserted.
|
||||||
|
# If they are defined in BaseHandler, explicitly replace them with our methods.
|
||||||
|
for name in ("get_current_user", "get_login_url"):
|
||||||
|
if name in BaseHandler.__dict__:
|
||||||
|
log.debug(
|
||||||
|
f"Overriding {BaseHandler}.{name} with HubAuthenticatedHandler.{name}"
|
||||||
|
)
|
||||||
|
method = getattr(HubAuthenticatedHandler, name)
|
||||||
|
setattr(BaseHandler, name, method)
|
||||||
|
return BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
def _patch_app_base_handlers(app):
|
||||||
|
"""Patch Hub Authentication into the base handlers of an app
|
||||||
|
|
||||||
|
Patches HubAuthenticatedHandler into:
|
||||||
|
|
||||||
|
- App.base_handler_class (if defined)
|
||||||
|
- jupyter_server's JupyterHandler (if already imported)
|
||||||
|
- notebook's IPythonHandler (if already imported)
|
||||||
|
"""
|
||||||
|
BaseHandler = app_base_handler = getattr(app, "base_handler_class", None)
|
||||||
|
|
||||||
|
base_handlers = []
|
||||||
|
if BaseHandler is not None:
|
||||||
|
base_handlers.append(BaseHandler)
|
||||||
|
|
||||||
|
# patch juptyer_server and notebook handlers if they have been imported
|
||||||
|
for base_handler_name in [
|
||||||
|
"jupyter_server.base.handlers.JupyterHandler",
|
||||||
|
"notebook.base.handlers.IPythonHandler",
|
||||||
|
]:
|
||||||
|
modname, _ = base_handler_name.rsplit(".", 1)
|
||||||
|
if modname in sys.modules:
|
||||||
|
base_handlers.append(import_item(base_handler_name))
|
||||||
|
|
||||||
|
if not base_handlers:
|
||||||
|
pkg = detect_base_package(app.__class__)
|
||||||
|
if pkg == "jupyter_server":
|
||||||
|
BaseHandler = import_item("jupyter_server.base.handlers.JupyterHandler")
|
||||||
|
elif pkg == "notebook":
|
||||||
|
BaseHandler = import_item("notebook.base.handlers.IPythonHandler")
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
"{}.base_handler_class must be defined".format(app.__class__.__name__)
|
||||||
|
)
|
||||||
|
base_handlers.append(BaseHandler)
|
||||||
|
|
||||||
|
# patch-in HubAuthenticatedHandler to base handler classes
|
||||||
|
for BaseHandler in base_handlers:
|
||||||
|
patch_base_handler(BaseHandler)
|
||||||
|
|
||||||
|
# return the first entry
|
||||||
|
return base_handlers[0]
|
||||||
|
|
||||||
|
|
||||||
def make_singleuser_app(App):
|
def make_singleuser_app(App):
|
||||||
"""Make and return a singleuser notebook app
|
"""Make and return a singleuser notebook app
|
||||||
|
|
||||||
@@ -706,37 +806,7 @@ def make_singleuser_app(App):
|
|||||||
# detect base classes
|
# detect base classes
|
||||||
LoginHandler = empty_parent_app.login_handler_class
|
LoginHandler = empty_parent_app.login_handler_class
|
||||||
LogoutHandler = empty_parent_app.logout_handler_class
|
LogoutHandler = empty_parent_app.logout_handler_class
|
||||||
BaseHandler = getattr(empty_parent_app, "base_handler_class", None)
|
BaseHandler = _patch_app_base_handlers(empty_parent_app)
|
||||||
if BaseHandler is None:
|
|
||||||
pkg = detect_base_package(App)
|
|
||||||
if pkg == "jupyter_server":
|
|
||||||
BaseHandler = import_item("jupyter_server.base.handlers.JupyterHandler")
|
|
||||||
elif pkg == "notebook":
|
|
||||||
BaseHandler = import_item("notebook.base.handlers.IPythonHandler")
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
"{}.base_handler_class must be defined".format(App.__name__)
|
|
||||||
)
|
|
||||||
|
|
||||||
# patch-in HubAuthenticatedHandler to BaseHandler,
|
|
||||||
# so anything inheriting from BaseHandler uses Hub authentication
|
|
||||||
if HubAuthenticatedHandler not in BaseHandler.__bases__:
|
|
||||||
new_bases = (HubAuthenticatedHandler,) + BaseHandler.__bases__
|
|
||||||
log.debug(
|
|
||||||
f"Patching {BaseHandler}{BaseHandler.__bases__} -> {BaseHandler}{new_bases}"
|
|
||||||
)
|
|
||||||
BaseHandler.__bases__ = new_bases
|
|
||||||
# We've now inserted our class as a parent of BaseHandler,
|
|
||||||
# but we also need to ensure BaseHandler *itself* doesn't
|
|
||||||
# override the public tornado API methods we have inserted.
|
|
||||||
# If they are defined in BaseHandler, explicitly replace them with our methods.
|
|
||||||
for name in ("get_current_user", "get_login_url"):
|
|
||||||
if name in BaseHandler.__dict__:
|
|
||||||
log.debug(
|
|
||||||
f"Overriding {BaseHandler}.{name} with HubAuthenticatedHandler.{name}"
|
|
||||||
)
|
|
||||||
method = getattr(HubAuthenticatedHandler, name)
|
|
||||||
setattr(BaseHandler, name, method)
|
|
||||||
|
|
||||||
# create Handler classes from mixins + bases
|
# create Handler classes from mixins + bases
|
||||||
class JupyterHubLoginHandler(JupyterHubLoginHandlerMixin, LoginHandler):
|
class JupyterHubLoginHandler(JupyterHubLoginHandlerMixin, LoginHandler):
|
||||||
@@ -766,4 +836,11 @@ def make_singleuser_app(App):
|
|||||||
logout_handler_class = JupyterHubLogoutHandler
|
logout_handler_class = JupyterHubLogoutHandler
|
||||||
oauth_callback_handler_class = OAuthCallbackHandler
|
oauth_callback_handler_class = OAuthCallbackHandler
|
||||||
|
|
||||||
|
def initialize(self, *args, **kwargs):
|
||||||
|
result = super().initialize(*args, **kwargs)
|
||||||
|
# run patch again after initialize, so extensions have already been loaded
|
||||||
|
# probably a no-op most of the time
|
||||||
|
_patch_app_base_handlers(self)
|
||||||
|
return result
|
||||||
|
|
||||||
return SingleUserNotebookApp
|
return SingleUserNotebookApp
|
||||||
|
@@ -1141,6 +1141,18 @@ class Spawner(LoggingConfigurable):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("Override in subclass. Must be a coroutine.")
|
raise NotImplementedError("Override in subclass. Must be a coroutine.")
|
||||||
|
|
||||||
|
def delete_forever(self):
|
||||||
|
"""Called when a user or server is deleted.
|
||||||
|
|
||||||
|
This can do things like request removal of resources such as persistent storage.
|
||||||
|
Only called on stopped spawners, and is usually the last action ever taken for the user.
|
||||||
|
|
||||||
|
Will only be called once on each Spawner, immediately prior to removal.
|
||||||
|
|
||||||
|
Stopping a server does *not* call this method.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def add_poll_callback(self, callback, *args, **kwargs):
|
def add_poll_callback(self, callback, *args, **kwargs):
|
||||||
"""Add a callback to fire when the single-user server stops"""
|
"""Add a callback to fire when the single-user server stops"""
|
||||||
if args or kwargs:
|
if args or kwargs:
|
||||||
|
@@ -93,16 +93,6 @@ class MockSpawner(SimpleLocalProcessSpawner):
|
|||||||
def _cmd_default(self):
|
def _cmd_default(self):
|
||||||
return [sys.executable, '-m', 'jupyterhub.tests.mocksu']
|
return [sys.executable, '-m', 'jupyterhub.tests.mocksu']
|
||||||
|
|
||||||
async def delete_forever(self):
|
|
||||||
"""Called when a user is deleted.
|
|
||||||
|
|
||||||
This can do things like request removal of resources such as persistent storage.
|
|
||||||
Only called on stopped spawners, and is likely the last action ever taken for the user.
|
|
||||||
|
|
||||||
Will only be called once on the user's default Spawner.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
use_this_api_token = None
|
use_this_api_token = None
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
@@ -252,6 +252,35 @@ class User:
|
|||||||
await self.save_auth_state(auth_state)
|
await self.save_auth_state(auth_state)
|
||||||
return auth_state
|
return auth_state
|
||||||
|
|
||||||
|
async def delete_spawners(self):
|
||||||
|
"""Call spawner cleanup methods
|
||||||
|
|
||||||
|
Allows the spawner to cleanup persistent resources
|
||||||
|
"""
|
||||||
|
for name in self.orm_user.orm_spawners.keys():
|
||||||
|
await self._delete_spawner(name)
|
||||||
|
|
||||||
|
async def _delete_spawner(self, name_or_spawner):
|
||||||
|
"""Delete a single spawner"""
|
||||||
|
# always ensure full Spawner
|
||||||
|
# this may instantiate the Spawner if it wasn't already running,
|
||||||
|
# just to delete it
|
||||||
|
if isinstance(name_or_spawner, str):
|
||||||
|
spawner = self.spawners[name_or_spawner]
|
||||||
|
else:
|
||||||
|
spawner = name_or_spawner
|
||||||
|
|
||||||
|
if spawner.active:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Spawner {spawner._log_name} is active and cannot be deleted."
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await maybe_future(spawner.delete_forever())
|
||||||
|
except Exception as e:
|
||||||
|
self.log.exception(
|
||||||
|
f"Error cleaning up persistent resources on {spawner._log_name}"
|
||||||
|
)
|
||||||
|
|
||||||
def all_spawners(self, include_default=True):
|
def all_spawners(self, include_default=True):
|
||||||
"""Generator yielding all my spawners
|
"""Generator yielding all my spawners
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ FROM $BASE_IMAGE
|
|||||||
MAINTAINER Project Jupyter <jupyter@googlegroups.com>
|
MAINTAINER Project Jupyter <jupyter@googlegroups.com>
|
||||||
|
|
||||||
ADD install_jupyterhub /tmp/install_jupyterhub
|
ADD install_jupyterhub /tmp/install_jupyterhub
|
||||||
ARG JUPYTERHUB_VERSION=master
|
ARG JUPYTERHUB_VERSION=main
|
||||||
# install pinned jupyterhub and ensure notebook is installed
|
# install pinned jupyterhub and ensure notebook is installed
|
||||||
RUN python3 /tmp/install_jupyterhub && \
|
RUN python3 /tmp/install_jupyterhub && \
|
||||||
python3 -m pip install notebook
|
python3 -m pip install notebook
|
||||||
|
@@ -5,7 +5,7 @@ Built from the `jupyter/base-notebook` base image.
|
|||||||
This image contains a single user notebook server for use with
|
This image contains a single user notebook server for use with
|
||||||
[JupyterHub](https://github.com/jupyterhub/jupyterhub). In particular, it is meant
|
[JupyterHub](https://github.com/jupyterhub/jupyterhub). In particular, it is meant
|
||||||
to be used with the
|
to be used with the
|
||||||
[DockerSpawner](https://github.com/jupyterhub/dockerspawner/blob/master/dockerspawner/dockerspawner.py)
|
[DockerSpawner](https://github.com/jupyterhub/dockerspawner/blob/HEAD/dockerspawner/dockerspawner.py)
|
||||||
class to launch user notebook servers within docker containers.
|
class to launch user notebook servers within docker containers.
|
||||||
|
|
||||||
The only thing this image accomplishes is pinning the jupyterhub version on top of base-notebook.
|
The only thing this image accomplishes is pinning the jupyterhub version on top of base-notebook.
|
||||||
|
@@ -13,7 +13,7 @@ function get_hub_version() {
|
|||||||
hub_xy="${hub_xy}.${split[3]}"
|
hub_xy="${hub_xy}.${split[3]}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
# tag e.g. 0.9 with master
|
# tag e.g. 0.9 with main
|
||||||
get_hub_version
|
get_hub_version
|
||||||
docker tag $DOCKER_REPO:$DOCKER_TAG $DOCKER_REPO:$hub_xy
|
docker tag $DOCKER_REPO:$DOCKER_TAG $DOCKER_REPO:$hub_xy
|
||||||
docker push $DOCKER_REPO:$hub_xy
|
docker push $DOCKER_REPO:$hub_xy
|
||||||
|
@@ -9,8 +9,8 @@ pip_install = [
|
|||||||
sys.executable, '-m', 'pip', 'install', '--no-cache', '--upgrade',
|
sys.executable, '-m', 'pip', 'install', '--no-cache', '--upgrade',
|
||||||
'--upgrade-strategy', 'only-if-needed',
|
'--upgrade-strategy', 'only-if-needed',
|
||||||
]
|
]
|
||||||
if V == 'master':
|
if V in {'main', 'HEAD'}:
|
||||||
req = 'https://github.com/jupyterhub/jupyterhub/archive/master.tar.gz'
|
req = 'https://github.com/jupyterhub/jupyterhub/archive/HEAD.tar.gz'
|
||||||
else:
|
else:
|
||||||
version_info = [ int(part) for part in V.split('.') ]
|
version_info = [ int(part) for part in V.split('.') ]
|
||||||
version_info[-1] += 1
|
version_info[-1] += 1
|
||||||
|
Reference in New Issue
Block a user