From d1ff010ab9a01bbb40615ec20ef5a84850351d98 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 11:45:30 +0530 Subject: [PATCH 01/18] Migrate start-notebook.sh to bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on > Stop using bash, haha 👍 from https://github.com/jupyter/docker-stacks/issues/1532. If there's more apetite for this, I'll try to migrate `start.sh` and `start-singleuser.sh` as well - I think they should all be merged together. We can remove the `.sh` suffixes for accuracy, and keep symlinks in so old config still works. Since the shebang is what is used to launch the correct interpreter, the `.sh` doesn't matter. Will help fix https://github.com/jupyter/docker-stacks/issues/1532, as I believe all those things are going to be easier to do from python than bash --- images/base-notebook/start-notebook.sh | 50 ++++++++++++++++++-------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/images/base-notebook/start-notebook.sh b/images/base-notebook/start-notebook.sh index 4f673d22a5..87539991b4 100755 --- a/images/base-notebook/start-notebook.sh +++ b/images/base-notebook/start-notebook.sh @@ -1,22 +1,42 @@ -#!/bin/bash +#!/usr/bin/env python -u # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. +import os +import sys +import shlex -set -e -# The Jupyter command to launch -# JupyterLab by default -DOCKER_STACKS_JUPYTER_CMD="${DOCKER_STACKS_JUPYTER_CMD:=lab}" +# If we are in a JupyterHub, we pass on to `start-singleuser.sh` instead so it does the right thing +if "JUPYTERHUB_API_TOKEN" not in os.environ: + print( + "WARNING: using start-singleuser.sh instead of start-notebook.sh to start a server associated with JupyterHub.", + file=sys.stderr, + ) + os.execvp("/usr/local/bin/start-singleuser.sh", sys.argv[1:]) -if [[ -n "${JUPYTERHUB_API_TOKEN}" ]]; then - echo "WARNING: using start-singleuser.sh instead of start-notebook.sh to start a server associated with JupyterHub." - exec /usr/local/bin/start-singleuser.sh "$@" -fi -wrapper="" -if [[ "${RESTARTABLE}" == "yes" ]]; then - wrapper="run-one-constantly" -fi +# Wrap everything in start.sh, no matter what +command = ["/usr/local/bin/start.sh"] -# shellcheck disable=SC1091,SC2086 -exec /usr/local/bin/start.sh ${wrapper} jupyter ${DOCKER_STACKS_JUPYTER_CMD} ${NOTEBOOK_ARGS} "$@" +# If we want to survive restarts, tell that to start.sh +if os.environ.get("RESTARTABLE") == "yes": + command.append("run-one-constantly") + +# We always launch a jupyter subcommand from this script +command.append("jupyter") + +# Launch the configured subcommand. Note that this should be a single string, so we don't split it +# We default to lab +jupyter_command = os.environ.get("DOCKER_STACKS_JUPYTER_CMD", "lab") +command.append(jupyter_command) + +# Append any optional NOTEBOOK_ARGS we were passed in. This is supposed to be multiple args passed +# on to the notebook command, so we split it correctly with shlex +command += ( + [] + if "NOTEBOOK_ARGS" not in os.environ + else shlex.split(os.environ["NOTEBOOK_ARGS"]) +) + +# Execute the command! +os.execvp(command[0], command) From ed704632262fa204d00a1437ca7fb06949d7b25d Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 11:58:25 +0530 Subject: [PATCH 02/18] Rename start-notebook.sh to start-notebook --- README.md | 2 +- docs/using/common.md | 18 +++++++++--------- docs/using/recipes.md | 6 +++--- docs/using/running.md | 2 +- docs/using/selecting.md | 2 +- .../notebook/letsencrypt-notebook.yml | 2 +- examples/make-deploy/Makefile | 2 +- examples/openshift/templates.json | 2 +- examples/source-to-image/README.md | 2 +- examples/source-to-image/run | 2 +- examples/source-to-image/templates.json | 2 +- images/base-notebook/Dockerfile | 7 +++++-- .../{start-notebook.sh => start-notebook} | 5 ++--- tests/base-notebook/test_container_options.py | 4 ++-- tests/base-notebook/test_healthcheck.py | 18 +++++++++--------- tests/pluto_check.py | 2 +- 16 files changed, 40 insertions(+), 38 deletions(-) rename images/base-notebook/{start-notebook.sh => start-notebook} (95%) diff --git a/README.md b/README.md index ada720e289..cee2b5a981 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ system when the container exits, but any changes made to the `~/work` directory By default, [jupyter's root_dir](https://jupyter-server.readthedocs.io/en/latest/other/full-config.html) is `/home/jovyan`. So, new notebooks will be saved there, unless you change the directory in the file browser. -To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook.sh --ServerApp.root_dir=/home/jovyan/work`. +To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook --ServerApp.root_dir=/home/jovyan/work`. ``` ## Contributing diff --git a/docs/using/common.md b/docs/using/common.md index dab66fed72..4172c88629 100644 --- a/docs/using/common.md +++ b/docs/using/common.md @@ -1,14 +1,14 @@ # Common Features Except for `jupyter/docker-stacks-foundation`, a container launched from any Jupyter Docker Stacks image runs a Jupyter Server with the JupyterLab frontend. -The container does so by executing a `start-notebook.sh` script. +The container does so by executing a `start-notebook` script. This script configures the internal container environment and then runs `jupyter lab`, passing any command-line arguments received. This page describes the options supported by the startup script and how to bypass it to run alternative commands. ## Jupyter Server Options -You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html) to the `start-notebook.sh` script when launching the container. +You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html) to the `start-notebook` script when launching the container. 1. For example, to secure the Jupyter Server with a [custom password](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html#preparing-a-hashed-password) hashed using `jupyter_server.auth.passwd()` instead of the default token, @@ -16,19 +16,19 @@ You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/l ```bash docker run -it --rm -p 8888:8888 jupyter/base-notebook \ - start-notebook.sh --PasswordIdentityProvider.hashed_password='argon2:$argon2id$v=19$m=10240,t=10,p=8$JdAN3fe9J45NvK/EPuGCvA$O/tbxglbwRpOFuBNTYrymAEH6370Q2z+eS1eF4GM6Do' + start-notebook --PasswordIdentityProvider.hashed_password='argon2:$argon2id$v=19$m=10240,t=10,p=8$JdAN3fe9J45NvK/EPuGCvA$O/tbxglbwRpOFuBNTYrymAEH6370Q2z+eS1eF4GM6Do' ``` 2. To set the [base URL](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html#running-the-notebook-with-a-customized-url-prefix) of the Jupyter Server, you can run the following: ```bash docker run -it --rm -p 8888:8888 jupyter/base-notebook \ - start-notebook.sh --ServerApp.base_url=/customized/url/prefix/ + start-notebook --ServerApp.base_url=/customized/url/prefix/ ``` ## Docker Options -You may instruct the `start-notebook.sh` script to customize the container environment before launching the Server. +You may instruct the `start-notebook` script to customize the container environment before launching the Server. You do so by passing arguments to the `docker run` command. ### User-related configurations @@ -104,7 +104,7 @@ You do so by passing arguments to the `docker run` command. You do **not** need this option to allow the user to `conda` or `pip` install additional packages. This option is helpful for cases when you wish to give `${NB_USER}` the ability to install OS packages with `apt` or modify other root-owned files in the container. You **must** run the container with `--user root` for this option to take effect. - (The `start-notebook.sh` script will `su ${NB_USER}` after adding `${NB_USER}` to sudoers.) + (The `start-notebook` script will `su ${NB_USER}` after adding `${NB_USER}` to sudoers.) **You should only enable `sudo` if you trust the user or if the container runs on an isolated host.** ### Additional runtime configurations @@ -147,7 +147,7 @@ For example, to mount a host folder containing a `notebook.key` and `notebook.cr docker run -it --rm -p 8888:8888 \ -v /some/host/folder:/etc/ssl/notebook \ jupyter/base-notebook \ - start-notebook.sh \ + start-notebook \ --ServerApp.keyfile=/etc/ssl/notebook/notebook.key \ --ServerApp.certfile=/etc/ssl/notebook/notebook.crt ``` @@ -159,7 +159,7 @@ For example: docker run -it --rm -p 8888:8888 \ -v /some/host/folder/notebook.pem:/etc/ssl/notebook.pem \ jupyter/base-notebook \ - start-notebook.sh \ + start-notebook \ --ServerApp.certfile=/etc/ssl/notebook.pem ``` @@ -220,7 +220,7 @@ docker run -it --rm \ ### `start.sh` -The `start-notebook.sh` script inherits most of its option handling capability from a more generic `start.sh` script. +The `start-notebook` script inherits most of its option handling capability from a more generic `start.sh` script. The `start.sh` script supports all the features described above but allows you to specify an arbitrary command to execute. For example, to run the text-based `ipython` console in a container, do the following: diff --git a/docs/using/recipes.md b/docs/using/recipes.md index cfeb4cedf5..2a457f86d1 100644 --- a/docs/using/recipes.md +++ b/docs/using/recipes.md @@ -375,14 +375,14 @@ Credit: [britishbadger](https://github.com/britishbadger) from [docker-stacks/is The default security is very good. There are use cases, encouraged by containers, where the jupyter container and the system it runs within lie inside the security boundary. It is convenient to launch the server without a password or token in these use cases. -In this case, you should use the `start-notebook.sh` script to launch the server with no token: +In this case, you should use the `start-notebook` script to launch the server with no token: For JupyterLab: ```bash docker run -it --rm \ jupyter/base-notebook \ - start-notebook.sh --IdentityProvider.token='' + start-notebook --IdentityProvider.token='' ``` For Jupyter Notebook: @@ -391,7 +391,7 @@ For Jupyter Notebook: docker run -it --rm \ -e DOCKER_STACKS_JUPYTER_CMD=notebook \ jupyter/base-notebook \ - start-notebook.sh --IdentityProvider.token='' + start-notebook --IdentityProvider.token='' ``` ## Enable nbclassic-extension spellchecker for markdown (or any other nbclassic-extension) diff --git a/docs/using/running.md b/docs/using/running.md index 4e764184ce..ad70c16f2b 100644 --- a/docs/using/running.md +++ b/docs/using/running.md @@ -69,7 +69,7 @@ Any other changes made in the container will be lost. By default, [jupyter's root_dir](https://jupyter-server.readthedocs.io/en/latest/other/full-config.html) is `/home/jovyan`. So, new notebooks will be saved there, unless you change the directory in the file browser. -To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook.sh --ServerApp.root_dir=/home/jovyan/work`. +To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook --ServerApp.root_dir=/home/jovyan/work`. ``` ### Example 3 diff --git a/docs/using/selecting.md b/docs/using/selecting.md index 450e463a8c..cf7e465b9d 100644 --- a/docs/using/selecting.md +++ b/docs/using/selecting.md @@ -56,7 +56,7 @@ It contains: - Everything in `jupyter/docker-stacks-foundation` - Minimally functional Server (e.g., no LaTeX support for saving notebooks as PDFs) - `notebook`, `jupyterhub` and `jupyterlab` packages -- A `start-notebook.sh` script as the default command +- A `start-notebook` script as the default command - A `start-singleuser.sh` script useful for launching containers in JupyterHub - Options for a self-signed HTTPS certificate diff --git a/examples/docker-compose/notebook/letsencrypt-notebook.yml b/examples/docker-compose/notebook/letsencrypt-notebook.yml index 1c47c99e0f..af52f16261 100644 --- a/examples/docker-compose/notebook/letsencrypt-notebook.yml +++ b/examples/docker-compose/notebook/letsencrypt-notebook.yml @@ -18,7 +18,7 @@ services: USE_HTTPS: "yes" PASSWORD: ${PASSWORD} command: > - start-notebook.sh + start-notebook --ServerApp.certfile=/etc/letsencrypt/fullchain.pem --ServerApp.keyfile=/etc/letsencrypt/privkey.pem diff --git a/examples/make-deploy/Makefile b/examples/make-deploy/Makefile index e937621c60..1c926fb678 100644 --- a/examples/make-deploy/Makefile +++ b/examples/make-deploy/Makefile @@ -13,7 +13,7 @@ define RUN_NOTEBOOK --name $(NAME) \ -v $(WORK_VOLUME):/home/jovyan/work \ $(DOCKER_ARGS) \ - $(IMAGE) bash -c "$(PRE_CMD) chown jovyan /home/jovyan/work && start-notebook.sh $(ARGS)" > /dev/null + $(IMAGE) bash -c "$(PRE_CMD) chown jovyan /home/jovyan/work && start-notebook $(ARGS)" > /dev/null @echo "DONE: Notebook '$(NAME)' listening on $$(docker-machine ip $$(docker-machine active)):$(PORT)" endef diff --git a/examples/openshift/templates.json b/examples/openshift/templates.json index 6c48e129be..adfb18c2af 100644 --- a/examples/openshift/templates.json +++ b/examples/openshift/templates.json @@ -80,7 +80,7 @@ "name": "jupyter-notebook", "image": "${NOTEBOOK_IMAGE}", "command": [ - "start-notebook.sh", + "start-notebook", "--config=/etc/jupyter/openshift/jupyter_server_config.py", "--no-browser", "--ip=0.0.0.0" diff --git a/examples/source-to-image/README.md b/examples/source-to-image/README.md index 1e9d3ba992..6513dbfa94 100644 --- a/examples/source-to-image/README.md +++ b/examples/source-to-image/README.md @@ -117,7 +117,7 @@ with the extra system packages, and then use that image with the S2I build to co The `run` script in this directory is very simple and just runs the notebook application. ```bash -exec start-notebook.sh "$@" +exec start-notebook "$@" ``` ## Integration with OpenShift diff --git a/examples/source-to-image/run b/examples/source-to-image/run index b5b641b8f6..b2a67c0f02 100755 --- a/examples/source-to-image/run +++ b/examples/source-to-image/run @@ -2,4 +2,4 @@ # Start up the notebook instance. -exec start-notebook.sh "$@" +exec start-notebook "$@" diff --git a/examples/source-to-image/templates.json b/examples/source-to-image/templates.json index e335fc6877..b069c88a86 100644 --- a/examples/source-to-image/templates.json +++ b/examples/source-to-image/templates.json @@ -274,7 +274,7 @@ "name": "jupyter-notebook", "image": "${APPLICATION_NAME}:latest", "command": [ - "start-notebook.sh", + "start-notebook", "--config=/etc/jupyter/openshift/jupyter_server_config.py", "--no-browser", "--ip=0.0.0.0" diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index 5f47a5d425..0f57e52d41 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -52,12 +52,15 @@ ENV JUPYTER_PORT=8888 EXPOSE $JUPYTER_PORT # Configure container startup -CMD ["start-notebook.sh"] +CMD ["start-notebook"] # Copy local files as late as possible to avoid cache busting -COPY start-notebook.sh start-singleuser.sh /usr/local/bin/ +COPY start-notebook start-singleuser.sh /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ +# Symlink start-notebook to start-notebook for backwards compatibility +RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook + # Fix permissions on /etc/jupyter as root USER root RUN fix-permissions /etc/jupyter/ diff --git a/images/base-notebook/start-notebook.sh b/images/base-notebook/start-notebook similarity index 95% rename from images/base-notebook/start-notebook.sh rename to images/base-notebook/start-notebook index 87539991b4..eca68a05b8 100755 --- a/images/base-notebook/start-notebook.sh +++ b/images/base-notebook/start-notebook @@ -2,14 +2,13 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import os -import sys import shlex - +import sys # If we are in a JupyterHub, we pass on to `start-singleuser.sh` instead so it does the right thing if "JUPYTERHUB_API_TOKEN" not in os.environ: print( - "WARNING: using start-singleuser.sh instead of start-notebook.sh to start a server associated with JupyterHub.", + "WARNING: using start-singleuser.sh instead of start-notebook to start a server associated with JupyterHub.", file=sys.stderr, ) os.execvp("/usr/local/bin/start-singleuser.sh", sys.argv[1:]) diff --git a/tests/base-notebook/test_container_options.py b/tests/base-notebook/test_container_options.py index 5fa28d8913..d9fd206796 100644 --- a/tests/base-notebook/test_container_options.py +++ b/tests/base-notebook/test_container_options.py @@ -15,7 +15,7 @@ def test_cli_args(container: TrackedContainer, http_client: requests.Session) -> """Image should respect command line args (e.g., disabling token security)""" host_port = find_free_port() running_container = container.run_detached( - command=["start-notebook.sh", "--IdentityProvider.token=''"], + command=["start-notebook", "--IdentityProvider.token=''"], ports={"8888/tcp": host_port}, ) resp = http_client.get(f"http://localhost:{host_port}") @@ -102,7 +102,7 @@ def test_custom_internal_port( host_port = find_free_port() internal_port = env.get("JUPYTER_PORT", 8888) running_container = container.run_detached( - command=["start-notebook.sh", "--IdentityProvider.token=''"], + command=["start-notebook", "--IdentityProvider.token=''"], environment=env, ports={internal_port: host_port}, ) diff --git a/tests/base-notebook/test_healthcheck.py b/tests/base-notebook/test_healthcheck.py index 73de100303..40af5203e8 100644 --- a/tests/base-notebook/test_healthcheck.py +++ b/tests/base-notebook/test_healthcheck.py @@ -22,23 +22,23 @@ (["RESTARTABLE=yes"], None, None), (["JUPYTER_PORT=8171"], None, None), (["JUPYTER_PORT=8117", "DOCKER_STACKS_JUPYTER_CMD=notebook"], None, None), - (None, ["start-notebook.sh", "--ServerApp.base_url=/test"], None), - (None, ["start-notebook.sh", "--ServerApp.base_url=/test/"], None), - (["GEN_CERT=1"], ["start-notebook.sh", "--ServerApp.base_url=/test"], None), + (None, ["start-notebook", "--ServerApp.base_url=/test"], None), + (None, ["start-notebook", "--ServerApp.base_url=/test/"], None), + (["GEN_CERT=1"], ["start-notebook", "--ServerApp.base_url=/test"], None), ( ["GEN_CERT=1", "JUPYTER_PORT=7891"], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], None, ), (["NB_USER=testuser", "CHOWN_HOME=1"], None, "root"), ( ["NB_USER=testuser", "CHOWN_HOME=1"], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], "root", ), ( ["NB_USER=testuser", "CHOWN_HOME=1", "JUPYTER_PORT=8123"], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], "root", ), ], @@ -85,7 +85,7 @@ def test_health( "HTTPS_PROXY=host.docker.internal", "HTTP_PROXY=host.docker.internal", ], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], "root", ), ], @@ -122,12 +122,12 @@ def test_health_proxy( (["NB_USER=testuser", "CHOWN_HOME=1"], None, None), ( ["NB_USER=testuser", "CHOWN_HOME=1"], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], None, ), ( ["NB_USER=testuser", "CHOWN_HOME=1", "JUPYTER_PORT=8123"], - ["start-notebook.sh", "--ServerApp.base_url=/test"], + ["start-notebook", "--ServerApp.base_url=/test"], None, ), ], diff --git a/tests/pluto_check.py b/tests/pluto_check.py index 5fc269de2e..7b7705adf3 100644 --- a/tests/pluto_check.py +++ b/tests/pluto_check.py @@ -18,7 +18,7 @@ def check_pluto_proxy( token = secrets.token_hex() container.run_detached( command=[ - "start-notebook.sh", + "start-notebook", f"--IdentityProvider.token={token}", ], ports={"8888/tcp": host_port}, From 00e751ec70d29738b111fb59a3db5425d5ab2123 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 11:59:37 +0530 Subject: [PATCH 03/18] Cleanup start-notebook a little --- images/base-notebook/start-notebook | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index eca68a05b8..b908f6c55c 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -31,11 +31,8 @@ command.append(jupyter_command) # Append any optional NOTEBOOK_ARGS we were passed in. This is supposed to be multiple args passed # on to the notebook command, so we split it correctly with shlex -command += ( - [] - if "NOTEBOOK_ARGS" not in os.environ - else shlex.split(os.environ["NOTEBOOK_ARGS"]) -) +if "NOTEBOOK_ARGS" in os.environ: + command += shlex.split(os.environ["NOTEBOOK_ARGS"]) # Execute the command! os.execvp(command[0], command) From ea06768c45a6982c6dbc77483c69b4a9ee879aca Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:05:55 +0530 Subject: [PATCH 04/18] Fix typo --- images/base-notebook/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index 0f57e52d41..d888bbc31f 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -59,7 +59,7 @@ COPY start-notebook start-singleuser.sh /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ # Symlink start-notebook to start-notebook for backwards compatibility -RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook +RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh # Fix permissions on /etc/jupyter as root USER root From 14a1e8c191f24b7d1bee2cf53db2451f228808ab Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:10:42 +0530 Subject: [PATCH 05/18] Migrate start-singleuser as well --- docs/using/selecting.md | 2 +- images/base-notebook/Dockerfile | 5 +++-- images/base-notebook/start-notebook | 6 +++--- images/base-notebook/start-singleuser | 20 ++++++++++++++++++++ images/base-notebook/start-singleuser.sh | 13 ------------- tests/base-notebook/test_start_container.py | 2 +- 6 files changed, 28 insertions(+), 20 deletions(-) create mode 100755 images/base-notebook/start-singleuser delete mode 100755 images/base-notebook/start-singleuser.sh diff --git a/docs/using/selecting.md b/docs/using/selecting.md index cf7e465b9d..968efa61e0 100644 --- a/docs/using/selecting.md +++ b/docs/using/selecting.md @@ -57,7 +57,7 @@ It contains: - Minimally functional Server (e.g., no LaTeX support for saving notebooks as PDFs) - `notebook`, `jupyterhub` and `jupyterlab` packages - A `start-notebook` script as the default command -- A `start-singleuser.sh` script useful for launching containers in JupyterHub +- A `start-singleuser` script useful for launching containers in JupyterHub - Options for a self-signed HTTPS certificate ### jupyter/minimal-notebook diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index d888bbc31f..35387917ef 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -55,11 +55,12 @@ EXPOSE $JUPYTER_PORT CMD ["start-notebook"] # Copy local files as late as possible to avoid cache busting -COPY start-notebook start-singleuser.sh /usr/local/bin/ +COPY start-notebook start-singleuser /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ -# Symlink start-notebook to start-notebook for backwards compatibility +# Symlink scripts to their older .sh variants for backwards compatibility RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh +RUN ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh # Fix permissions on /etc/jupyter as root USER root diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index b908f6c55c..25d520bbd7 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -5,13 +5,13 @@ import os import shlex import sys -# If we are in a JupyterHub, we pass on to `start-singleuser.sh` instead so it does the right thing +# If we are in a JupyterHub, we pass on to `start-singleuser` instead so it does the right thing if "JUPYTERHUB_API_TOKEN" not in os.environ: print( - "WARNING: using start-singleuser.sh instead of start-notebook to start a server associated with JupyterHub.", + "WARNING: using start-singleuser instead of start-notebook to start a server associated with JupyterHub.", file=sys.stderr, ) - os.execvp("/usr/local/bin/start-singleuser.sh", sys.argv[1:]) + os.execvp("/usr/local/bin/start-singleuser", sys.argv[1:]) # Wrap everything in start.sh, no matter what diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser new file mode 100755 index 0000000000..aec1c888fe --- /dev/null +++ b/images/base-notebook/start-singleuser @@ -0,0 +1,20 @@ +#!/usr/bin/env python -u +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +import os +import shlex +import sys + +command = ["/usr/local/bin/start.sh"] + +# set default ip to 0.0.0.0 +if "--ip=" not in os.environ.get("NOTEBOOK_ARGS", ""): + command.append("--ip=0.0.0.0") + +# Append any optional NOTEBOOK_ARGS we were passed in. This is supposed to be multiple args passed +# on to the notebook command, so we split it correctly with shlex +if "NOTEBOOK_ARGS" in os.environ: + command += shlex.split(os.environ["NOTEBOOK_ARGS"]) + +# Execute the command! +os.execvp(command[0], command) diff --git a/images/base-notebook/start-singleuser.sh b/images/base-notebook/start-singleuser.sh deleted file mode 100755 index a2166e2c6d..0000000000 --- a/images/base-notebook/start-singleuser.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - -set -e - -# set default ip to 0.0.0.0 -if [[ "${NOTEBOOK_ARGS} $*" != *"--ip="* ]]; then - NOTEBOOK_ARGS="--ip=0.0.0.0 ${NOTEBOOK_ARGS}" -fi - -# shellcheck disable=SC1091,SC2086 -. /usr/local/bin/start.sh jupyterhub-singleuser ${NOTEBOOK_ARGS} "$@" diff --git a/tests/base-notebook/test_start_container.py b/tests/base-notebook/test_start_container.py index 556f4c2c7c..6ca781775f 100644 --- a/tests/base-notebook/test_start_container.py +++ b/tests/base-notebook/test_start_container.py @@ -25,7 +25,7 @@ ["JUPYTERHUB_API_TOKEN=my_token"], "jupyterhub-singleuser", False, - ["WARNING: using start-singleuser.sh"], + ["WARNING: using start-singleuser"], ), ], ) From 40f37d7968b2c1f07cc89dd95edf3f36b25b0479 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:12:42 +0530 Subject: [PATCH 06/18] Remove unused import --- images/base-notebook/start-singleuser | 1 - 1 file changed, 1 deletion(-) diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser index aec1c888fe..c3760c6a74 100755 --- a/images/base-notebook/start-singleuser +++ b/images/base-notebook/start-singleuser @@ -3,7 +3,6 @@ # Distributed under the terms of the Modified BSD License. import os import shlex -import sys command = ["/usr/local/bin/start.sh"] From 33642945ae7095272d731a11f90d6d0c6e85d018 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:21:18 +0530 Subject: [PATCH 07/18] Run symlink commands as root --- images/base-notebook/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index 35387917ef..66f8e9a572 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -58,14 +58,14 @@ CMD ["start-notebook"] COPY start-notebook start-singleuser /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ -# Symlink scripts to their older .sh variants for backwards compatibility -RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh -RUN ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh - # Fix permissions on /etc/jupyter as root USER root RUN fix-permissions /etc/jupyter/ +# Symlink scripts to their older .sh variants for backwards compatibility +RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh +RUN ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh + # HEALTHCHECK documentation: https://docs.docker.com/engine/reference/builder/#healthcheck # This healtcheck works well for `lab`, `notebook`, `nbclassic`, `server` and `retro` jupyter commands # https://github.com/jupyter/docker-stacks/issues/915#issuecomment-1068528799 From bb98d5e5c4c6124cac73331ee8f310a1e8b3032c Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:25:45 +0530 Subject: [PATCH 08/18] Combine repetitive RUN commands --- images/base-notebook/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index 66f8e9a572..0c6d429495 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -63,8 +63,8 @@ USER root RUN fix-permissions /etc/jupyter/ # Symlink scripts to their older .sh variants for backwards compatibility -RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh -RUN ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh +RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh && \ + ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh # HEALTHCHECK documentation: https://docs.docker.com/engine/reference/builder/#healthcheck # This healtcheck works well for `lab`, `notebook`, `nbclassic`, `server` and `retro` jupyter commands From 9e718398c80d150e84b1ce4fb463f6c7b46f9630 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 12:43:36 +0530 Subject: [PATCH 09/18] Remove multiple args to env -u can not be set by shebang, we must set the env var instead --- images/base-notebook/start-notebook | 2 +- images/base-notebook/start-singleuser | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index 25d520bbd7..af6edac4f0 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -1,4 +1,4 @@ -#!/usr/bin/env python -u +#!/usr/bin/env python # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import os diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser index c3760c6a74..0597921fd9 100755 --- a/images/base-notebook/start-singleuser +++ b/images/base-notebook/start-singleuser @@ -1,4 +1,4 @@ -#!/usr/bin/env python -u +#!/usr/bin/env python # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. import os From 67302e350c6019f1fbe1d40da835e7f8e1496251 Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Tue, 17 Oct 2023 15:27:27 +0530 Subject: [PATCH 10/18] Fix conditional inversion Co-authored-by: Ayaz Salikhov --- images/base-notebook/start-notebook | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index af6edac4f0..ac07e8cb6c 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -6,7 +6,7 @@ import shlex import sys # If we are in a JupyterHub, we pass on to `start-singleuser` instead so it does the right thing -if "JUPYTERHUB_API_TOKEN" not in os.environ: +if "JUPYTERHUB_API_TOKEN" in os.environ: print( "WARNING: using start-singleuser instead of start-notebook to start a server associated with JupyterHub.", file=sys.stderr, From 1804a8c5ea800461eab35357ae54af1dbc57027e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 15:38:57 +0530 Subject: [PATCH 11/18] Fix how start-singleuser is exec'd --- images/base-notebook/start-notebook | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index ac07e8cb6c..cc084b5fa5 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -11,7 +11,8 @@ if "JUPYTERHUB_API_TOKEN" in os.environ: "WARNING: using start-singleuser instead of start-notebook to start a server associated with JupyterHub.", file=sys.stderr, ) - os.execvp("/usr/local/bin/start-singleuser", sys.argv[1:]) + command = ["/usr/local/bin/start-singleuser"] + sys.argv[1:] + os.execvp(command[0], command) # Wrap everything in start.sh, no matter what From 527b7564c1d6c318cfcd56a0ae23cd5365499ca4 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 15:59:04 +0530 Subject: [PATCH 12/18] Actually call jupyterhub-singleuser in start-singleuser --- images/base-notebook/start-singleuser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser index 0597921fd9..c5a6061298 100755 --- a/images/base-notebook/start-singleuser +++ b/images/base-notebook/start-singleuser @@ -4,7 +4,7 @@ import os import shlex -command = ["/usr/local/bin/start.sh"] +command = ["/usr/local/bin/start.sh", "jupyterhub-singleuser"] # set default ip to 0.0.0.0 if "--ip=" not in os.environ.get("NOTEBOOK_ARGS", ""): From 9f7bdb391e7742afb4497285c874d9280d83aa13 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 15:59:37 +0530 Subject: [PATCH 13/18] Pass through any additional args we get --- images/base-notebook/start-notebook | 3 +++ images/base-notebook/start-singleuser | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook index cc084b5fa5..6ec8c3ca96 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook @@ -35,5 +35,8 @@ command.append(jupyter_command) if "NOTEBOOK_ARGS" in os.environ: command += shlex.split(os.environ["NOTEBOOK_ARGS"]) +# Pass through any other args we were passed on the commandline +command += sys.argv[1:] + # Execute the command! os.execvp(command[0], command) diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser index c5a6061298..2dcf6c091c 100755 --- a/images/base-notebook/start-singleuser +++ b/images/base-notebook/start-singleuser @@ -3,6 +3,7 @@ # Distributed under the terms of the Modified BSD License. import os import shlex +import sys command = ["/usr/local/bin/start.sh", "jupyterhub-singleuser"] @@ -15,5 +16,8 @@ if "--ip=" not in os.environ.get("NOTEBOOK_ARGS", ""): if "NOTEBOOK_ARGS" in os.environ: command += shlex.split(os.environ["NOTEBOOK_ARGS"]) +# Pass any other args we have been passed through +command += sys.argv[1:] + # Execute the command! os.execvp(command[0], command) From 37d5b15d3e525be2a664effa829650627023a828 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 16:13:43 +0530 Subject: [PATCH 14/18] Put .py suffix on the start-* scripts --- README.md | 2 +- docs/using/common.md | 18 +++++++++--------- docs/using/recipes.md | 6 +++--- docs/using/running.md | 2 +- docs/using/selecting.md | 4 ++-- .../notebook/letsencrypt-notebook.yml | 2 +- examples/make-deploy/Makefile | 2 +- examples/openshift/templates.json | 2 +- examples/source-to-image/README.md | 2 +- examples/source-to-image/run | 2 +- examples/source-to-image/templates.json | 2 +- images/base-notebook/Dockerfile | 8 ++++---- .../{start-notebook => start-notebook.py} | 7 +++---- .../{start-singleuser => start-singleuser.py} | 0 tests/base-notebook/test_container_options.py | 4 ++-- tests/base-notebook/test_healthcheck.py | 18 +++++++++--------- tests/base-notebook/test_start_container.py | 6 +++--- tests/pluto_check.py | 2 +- 18 files changed, 44 insertions(+), 45 deletions(-) rename images/base-notebook/{start-notebook => start-notebook.py} (79%) rename images/base-notebook/{start-singleuser => start-singleuser.py} (100%) diff --git a/README.md b/README.md index cee2b5a981..49934d917a 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ system when the container exits, but any changes made to the `~/work` directory By default, [jupyter's root_dir](https://jupyter-server.readthedocs.io/en/latest/other/full-config.html) is `/home/jovyan`. So, new notebooks will be saved there, unless you change the directory in the file browser. -To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook --ServerApp.root_dir=/home/jovyan/work`. +To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook.py --ServerApp.root_dir=/home/jovyan/work`. ``` ## Contributing diff --git a/docs/using/common.md b/docs/using/common.md index 4172c88629..e2fee1f503 100644 --- a/docs/using/common.md +++ b/docs/using/common.md @@ -1,14 +1,14 @@ # Common Features Except for `jupyter/docker-stacks-foundation`, a container launched from any Jupyter Docker Stacks image runs a Jupyter Server with the JupyterLab frontend. -The container does so by executing a `start-notebook` script. +The container does so by executing a `start-notebook.py` script. This script configures the internal container environment and then runs `jupyter lab`, passing any command-line arguments received. This page describes the options supported by the startup script and how to bypass it to run alternative commands. ## Jupyter Server Options -You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html) to the `start-notebook` script when launching the container. +You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html) to the `start-notebook.py` script when launching the container. 1. For example, to secure the Jupyter Server with a [custom password](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html#preparing-a-hashed-password) hashed using `jupyter_server.auth.passwd()` instead of the default token, @@ -16,19 +16,19 @@ You can pass [Jupyter Server options](https://jupyter-server.readthedocs.io/en/l ```bash docker run -it --rm -p 8888:8888 jupyter/base-notebook \ - start-notebook --PasswordIdentityProvider.hashed_password='argon2:$argon2id$v=19$m=10240,t=10,p=8$JdAN3fe9J45NvK/EPuGCvA$O/tbxglbwRpOFuBNTYrymAEH6370Q2z+eS1eF4GM6Do' + start-notebook.py --PasswordIdentityProvider.hashed_password='argon2:$argon2id$v=19$m=10240,t=10,p=8$JdAN3fe9J45NvK/EPuGCvA$O/tbxglbwRpOFuBNTYrymAEH6370Q2z+eS1eF4GM6Do' ``` 2. To set the [base URL](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html#running-the-notebook-with-a-customized-url-prefix) of the Jupyter Server, you can run the following: ```bash docker run -it --rm -p 8888:8888 jupyter/base-notebook \ - start-notebook --ServerApp.base_url=/customized/url/prefix/ + start-notebook.py --ServerApp.base_url=/customized/url/prefix/ ``` ## Docker Options -You may instruct the `start-notebook` script to customize the container environment before launching the Server. +You may instruct the `start-notebook.py` script to customize the container environment before launching the Server. You do so by passing arguments to the `docker run` command. ### User-related configurations @@ -104,7 +104,7 @@ You do so by passing arguments to the `docker run` command. You do **not** need this option to allow the user to `conda` or `pip` install additional packages. This option is helpful for cases when you wish to give `${NB_USER}` the ability to install OS packages with `apt` or modify other root-owned files in the container. You **must** run the container with `--user root` for this option to take effect. - (The `start-notebook` script will `su ${NB_USER}` after adding `${NB_USER}` to sudoers.) + (The `start-notebook.py` script will `su ${NB_USER}` after adding `${NB_USER}` to sudoers.) **You should only enable `sudo` if you trust the user or if the container runs on an isolated host.** ### Additional runtime configurations @@ -147,7 +147,7 @@ For example, to mount a host folder containing a `notebook.key` and `notebook.cr docker run -it --rm -p 8888:8888 \ -v /some/host/folder:/etc/ssl/notebook \ jupyter/base-notebook \ - start-notebook \ + start-notebook.py \ --ServerApp.keyfile=/etc/ssl/notebook/notebook.key \ --ServerApp.certfile=/etc/ssl/notebook/notebook.crt ``` @@ -159,7 +159,7 @@ For example: docker run -it --rm -p 8888:8888 \ -v /some/host/folder/notebook.pem:/etc/ssl/notebook.pem \ jupyter/base-notebook \ - start-notebook \ + start-notebook.py \ --ServerApp.certfile=/etc/ssl/notebook.pem ``` @@ -220,7 +220,7 @@ docker run -it --rm \ ### `start.sh` -The `start-notebook` script inherits most of its option handling capability from a more generic `start.sh` script. +The `start-notebook.py` script inherits most of its option handling capability from a more generic `start.sh` script. The `start.sh` script supports all the features described above but allows you to specify an arbitrary command to execute. For example, to run the text-based `ipython` console in a container, do the following: diff --git a/docs/using/recipes.md b/docs/using/recipes.md index 2a457f86d1..196cd425be 100644 --- a/docs/using/recipes.md +++ b/docs/using/recipes.md @@ -375,14 +375,14 @@ Credit: [britishbadger](https://github.com/britishbadger) from [docker-stacks/is The default security is very good. There are use cases, encouraged by containers, where the jupyter container and the system it runs within lie inside the security boundary. It is convenient to launch the server without a password or token in these use cases. -In this case, you should use the `start-notebook` script to launch the server with no token: +In this case, you should use the `start-notebook.py` script to launch the server with no token: For JupyterLab: ```bash docker run -it --rm \ jupyter/base-notebook \ - start-notebook --IdentityProvider.token='' + start-notebook.py --IdentityProvider.token='' ``` For Jupyter Notebook: @@ -391,7 +391,7 @@ For Jupyter Notebook: docker run -it --rm \ -e DOCKER_STACKS_JUPYTER_CMD=notebook \ jupyter/base-notebook \ - start-notebook --IdentityProvider.token='' + start-notebook.py --IdentityProvider.token='' ``` ## Enable nbclassic-extension spellchecker for markdown (or any other nbclassic-extension) diff --git a/docs/using/running.md b/docs/using/running.md index ad70c16f2b..b888c04309 100644 --- a/docs/using/running.md +++ b/docs/using/running.md @@ -69,7 +69,7 @@ Any other changes made in the container will be lost. By default, [jupyter's root_dir](https://jupyter-server.readthedocs.io/en/latest/other/full-config.html) is `/home/jovyan`. So, new notebooks will be saved there, unless you change the directory in the file browser. -To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook --ServerApp.root_dir=/home/jovyan/work`. +To change the default directory, you will need to specify `ServerApp.root_dir` by adding this line to previous command: `start-notebook.py --ServerApp.root_dir=/home/jovyan/work`. ``` ### Example 3 diff --git a/docs/using/selecting.md b/docs/using/selecting.md index 968efa61e0..d81ad1bbbc 100644 --- a/docs/using/selecting.md +++ b/docs/using/selecting.md @@ -56,8 +56,8 @@ It contains: - Everything in `jupyter/docker-stacks-foundation` - Minimally functional Server (e.g., no LaTeX support for saving notebooks as PDFs) - `notebook`, `jupyterhub` and `jupyterlab` packages -- A `start-notebook` script as the default command -- A `start-singleuser` script useful for launching containers in JupyterHub +- A `start-notebook.py` script as the default command +- A `start-singleuser.py` script useful for launching containers in JupyterHub - Options for a self-signed HTTPS certificate ### jupyter/minimal-notebook diff --git a/examples/docker-compose/notebook/letsencrypt-notebook.yml b/examples/docker-compose/notebook/letsencrypt-notebook.yml index af52f16261..06bab31966 100644 --- a/examples/docker-compose/notebook/letsencrypt-notebook.yml +++ b/examples/docker-compose/notebook/letsencrypt-notebook.yml @@ -18,7 +18,7 @@ services: USE_HTTPS: "yes" PASSWORD: ${PASSWORD} command: > - start-notebook + start-notebook.py --ServerApp.certfile=/etc/letsencrypt/fullchain.pem --ServerApp.keyfile=/etc/letsencrypt/privkey.pem diff --git a/examples/make-deploy/Makefile b/examples/make-deploy/Makefile index 1c926fb678..aa62dd0bbf 100644 --- a/examples/make-deploy/Makefile +++ b/examples/make-deploy/Makefile @@ -13,7 +13,7 @@ define RUN_NOTEBOOK --name $(NAME) \ -v $(WORK_VOLUME):/home/jovyan/work \ $(DOCKER_ARGS) \ - $(IMAGE) bash -c "$(PRE_CMD) chown jovyan /home/jovyan/work && start-notebook $(ARGS)" > /dev/null + $(IMAGE) bash -c "$(PRE_CMD) chown jovyan /home/jovyan/work && start-notebook.py $(ARGS)" > /dev/null @echo "DONE: Notebook '$(NAME)' listening on $$(docker-machine ip $$(docker-machine active)):$(PORT)" endef diff --git a/examples/openshift/templates.json b/examples/openshift/templates.json index adfb18c2af..e12036ecb8 100644 --- a/examples/openshift/templates.json +++ b/examples/openshift/templates.json @@ -80,7 +80,7 @@ "name": "jupyter-notebook", "image": "${NOTEBOOK_IMAGE}", "command": [ - "start-notebook", + "start-notebook.py", "--config=/etc/jupyter/openshift/jupyter_server_config.py", "--no-browser", "--ip=0.0.0.0" diff --git a/examples/source-to-image/README.md b/examples/source-to-image/README.md index 6513dbfa94..8639c10999 100644 --- a/examples/source-to-image/README.md +++ b/examples/source-to-image/README.md @@ -117,7 +117,7 @@ with the extra system packages, and then use that image with the S2I build to co The `run` script in this directory is very simple and just runs the notebook application. ```bash -exec start-notebook "$@" +exec start-notebook.py "$@" ``` ## Integration with OpenShift diff --git a/examples/source-to-image/run b/examples/source-to-image/run index b2a67c0f02..556efdda9d 100755 --- a/examples/source-to-image/run +++ b/examples/source-to-image/run @@ -2,4 +2,4 @@ # Start up the notebook instance. -exec start-notebook "$@" +exec start-notebook.py "$@" diff --git a/examples/source-to-image/templates.json b/examples/source-to-image/templates.json index b069c88a86..8daa0823d0 100644 --- a/examples/source-to-image/templates.json +++ b/examples/source-to-image/templates.json @@ -274,7 +274,7 @@ "name": "jupyter-notebook", "image": "${APPLICATION_NAME}:latest", "command": [ - "start-notebook", + "start-notebook.py", "--config=/etc/jupyter/openshift/jupyter_server_config.py", "--no-browser", "--ip=0.0.0.0" diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index 0c6d429495..fd7a265f4c 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -52,10 +52,10 @@ ENV JUPYTER_PORT=8888 EXPOSE $JUPYTER_PORT # Configure container startup -CMD ["start-notebook"] +CMD ["start-notebook.py"] # Copy local files as late as possible to avoid cache busting -COPY start-notebook start-singleuser /usr/local/bin/ +COPY start-notebook.py start-singleuser.py /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ # Fix permissions on /etc/jupyter as root @@ -63,8 +63,8 @@ USER root RUN fix-permissions /etc/jupyter/ # Symlink scripts to their older .sh variants for backwards compatibility -RUN ln -s /usr/local/bin/start-notebook /usr/local/bin/start-notebook.sh && \ - ln -s /usr/local/bin/start-jupyterhub /usr/local/bin/start-jupyterhub.sh +RUN ln -s /usr/local/bin/start-notebook.py /usr/local/bin/start-notebook.sh && \ + ln -s /usr/local/bin/start-singleuser.py /usr/local/bin/start-singleuser.sh # HEALTHCHECK documentation: https://docs.docker.com/engine/reference/builder/#healthcheck # This healtcheck works well for `lab`, `notebook`, `nbclassic`, `server` and `retro` jupyter commands diff --git a/images/base-notebook/start-notebook b/images/base-notebook/start-notebook.py similarity index 79% rename from images/base-notebook/start-notebook rename to images/base-notebook/start-notebook.py index 6ec8c3ca96..db1efc246c 100755 --- a/images/base-notebook/start-notebook +++ b/images/base-notebook/start-notebook.py @@ -5,13 +5,12 @@ import shlex import sys -# If we are in a JupyterHub, we pass on to `start-singleuser` instead so it does the right thing +# If we are in a JupyterHub, we pass on to `start-singleuser.py` instead so it does the right thing if "JUPYTERHUB_API_TOKEN" in os.environ: print( - "WARNING: using start-singleuser instead of start-notebook to start a server associated with JupyterHub.", - file=sys.stderr, + "WARNING: using start-singleuser.py instead of start-notebook.py to start a server associated with JupyterHub." ) - command = ["/usr/local/bin/start-singleuser"] + sys.argv[1:] + command = ["/usr/local/bin/start-singleuser.py"] + sys.argv[1:] os.execvp(command[0], command) diff --git a/images/base-notebook/start-singleuser b/images/base-notebook/start-singleuser.py similarity index 100% rename from images/base-notebook/start-singleuser rename to images/base-notebook/start-singleuser.py diff --git a/tests/base-notebook/test_container_options.py b/tests/base-notebook/test_container_options.py index d9fd206796..1ea501d85c 100644 --- a/tests/base-notebook/test_container_options.py +++ b/tests/base-notebook/test_container_options.py @@ -15,7 +15,7 @@ def test_cli_args(container: TrackedContainer, http_client: requests.Session) -> """Image should respect command line args (e.g., disabling token security)""" host_port = find_free_port() running_container = container.run_detached( - command=["start-notebook", "--IdentityProvider.token=''"], + command=["start-notebook.py", "--IdentityProvider.token=''"], ports={"8888/tcp": host_port}, ) resp = http_client.get(f"http://localhost:{host_port}") @@ -102,7 +102,7 @@ def test_custom_internal_port( host_port = find_free_port() internal_port = env.get("JUPYTER_PORT", 8888) running_container = container.run_detached( - command=["start-notebook", "--IdentityProvider.token=''"], + command=["start-notebook.py", "--IdentityProvider.token=''"], environment=env, ports={internal_port: host_port}, ) diff --git a/tests/base-notebook/test_healthcheck.py b/tests/base-notebook/test_healthcheck.py index 40af5203e8..0e54d30038 100644 --- a/tests/base-notebook/test_healthcheck.py +++ b/tests/base-notebook/test_healthcheck.py @@ -22,23 +22,23 @@ (["RESTARTABLE=yes"], None, None), (["JUPYTER_PORT=8171"], None, None), (["JUPYTER_PORT=8117", "DOCKER_STACKS_JUPYTER_CMD=notebook"], None, None), - (None, ["start-notebook", "--ServerApp.base_url=/test"], None), - (None, ["start-notebook", "--ServerApp.base_url=/test/"], None), - (["GEN_CERT=1"], ["start-notebook", "--ServerApp.base_url=/test"], None), + (None, ["start-notebook.py", "--ServerApp.base_url=/test"], None), + (None, ["start-notebook.py", "--ServerApp.base_url=/test/"], None), + (["GEN_CERT=1"], ["start-notebook.py", "--ServerApp.base_url=/test"], None), ( ["GEN_CERT=1", "JUPYTER_PORT=7891"], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], None, ), (["NB_USER=testuser", "CHOWN_HOME=1"], None, "root"), ( ["NB_USER=testuser", "CHOWN_HOME=1"], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], "root", ), ( ["NB_USER=testuser", "CHOWN_HOME=1", "JUPYTER_PORT=8123"], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], "root", ), ], @@ -85,7 +85,7 @@ def test_health( "HTTPS_PROXY=host.docker.internal", "HTTP_PROXY=host.docker.internal", ], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], "root", ), ], @@ -122,12 +122,12 @@ def test_health_proxy( (["NB_USER=testuser", "CHOWN_HOME=1"], None, None), ( ["NB_USER=testuser", "CHOWN_HOME=1"], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], None, ), ( ["NB_USER=testuser", "CHOWN_HOME=1", "JUPYTER_PORT=8123"], - ["start-notebook", "--ServerApp.base_url=/test"], + ["start-notebook.py", "--ServerApp.base_url=/test"], None, ), ], diff --git a/tests/base-notebook/test_start_container.py b/tests/base-notebook/test_start_container.py index 6ca781775f..830b36c7e7 100644 --- a/tests/base-notebook/test_start_container.py +++ b/tests/base-notebook/test_start_container.py @@ -25,7 +25,7 @@ ["JUPYTERHUB_API_TOKEN=my_token"], "jupyterhub-singleuser", False, - ["WARNING: using start-singleuser"], + ["WARNING: using start-singleuser.py"], ), ], ) @@ -37,9 +37,9 @@ def test_start_notebook( expected_start: bool, expected_warnings: list[str], ) -> None: - """Test the notebook start-notebook script""" + """Test the notebook start-notebook.py script""" LOGGER.info( - f"Test that the start-notebook launches the {expected_command} server from the env {env} ..." + f"Test that the start-notebook.py launches the {expected_command} server from the env {env} ..." ) host_port = find_free_port() running_container = container.run_detached( diff --git a/tests/pluto_check.py b/tests/pluto_check.py index 7b7705adf3..ebb558b4be 100644 --- a/tests/pluto_check.py +++ b/tests/pluto_check.py @@ -18,7 +18,7 @@ def check_pluto_proxy( token = secrets.token_hex() container.run_detached( command=[ - "start-notebook", + "start-notebook.py", f"--IdentityProvider.token={token}", ], ports={"8888/tcp": host_port}, From 81c67ef7caef54776b2a3dc43e3ff7eb0c2f894e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 16:15:40 +0530 Subject: [PATCH 15/18] Add .sh shims for the start-* scripts --- images/base-notebook/Dockerfile | 6 +----- images/base-notebook/start-notebook.sh | 5 +++++ images/base-notebook/start-singleuser.sh | 5 +++++ 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100755 images/base-notebook/start-notebook.sh create mode 100755 images/base-notebook/start-singleuser.sh diff --git a/images/base-notebook/Dockerfile b/images/base-notebook/Dockerfile index fd7a265f4c..03c6eed9d9 100644 --- a/images/base-notebook/Dockerfile +++ b/images/base-notebook/Dockerfile @@ -55,17 +55,13 @@ EXPOSE $JUPYTER_PORT CMD ["start-notebook.py"] # Copy local files as late as possible to avoid cache busting -COPY start-notebook.py start-singleuser.py /usr/local/bin/ +COPY start-notebook.py start-notebook.sh start-singleuser.py start-singleuser.sh /usr/local/bin/ COPY jupyter_server_config.py docker_healthcheck.py /etc/jupyter/ # Fix permissions on /etc/jupyter as root USER root RUN fix-permissions /etc/jupyter/ -# Symlink scripts to their older .sh variants for backwards compatibility -RUN ln -s /usr/local/bin/start-notebook.py /usr/local/bin/start-notebook.sh && \ - ln -s /usr/local/bin/start-singleuser.py /usr/local/bin/start-singleuser.sh - # HEALTHCHECK documentation: https://docs.docker.com/engine/reference/builder/#healthcheck # This healtcheck works well for `lab`, `notebook`, `nbclassic`, `server` and `retro` jupyter commands # https://github.com/jupyter/docker-stacks/issues/915#issuecomment-1068528799 diff --git a/images/base-notebook/start-notebook.sh b/images/base-notebook/start-notebook.sh new file mode 100755 index 0000000000..c47ebba334 --- /dev/null +++ b/images/base-notebook/start-notebook.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Shim to emit warning and call start-notebook.py +echo "WARNING: Use start-notebook.py instead" + +exec /usr/local/bin/start-notebook.py "$@" diff --git a/images/base-notebook/start-singleuser.sh b/images/base-notebook/start-singleuser.sh new file mode 100755 index 0000000000..ecf0e068ae --- /dev/null +++ b/images/base-notebook/start-singleuser.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Shim to emit warning and call start-singleuser.py +echo "WARNING: Use start-singleuser.py instead" + +exec /usr/local/bin/start-singleuser.py "$@" From 219cf3633810a80b341a435e50c9304277e02beb Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 16:30:30 +0530 Subject: [PATCH 16/18] Document start-notebook.sh and start-singleuser.sh --- docs/using/selecting.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/using/selecting.md b/docs/using/selecting.md index d81ad1bbbc..f77d9abb87 100644 --- a/docs/using/selecting.md +++ b/docs/using/selecting.md @@ -60,6 +60,13 @@ It contains: - A `start-singleuser.py` script useful for launching containers in JupyterHub - Options for a self-signed HTTPS certificate +```{warning} +`jupyter/base-notebook` also contains `start-notebook.sh` and `start-singleuser.sh` files to maintain backwards +compatibility. External config that explicitly refers to those files should instead update to refer to +`start-notebook.py` and `start-singleuser.py` instead. The shim `.sh` files will be removed at some future +date. +``` + ### jupyter/minimal-notebook [Source on GitHub](https://github.com/jupyter/docker-stacks/tree/main/images/minimal-notebook) | From d6519ac8312c35f30aa9cdca18dc97607e1300d3 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 17 Oct 2023 16:30:46 +0530 Subject: [PATCH 17/18] Partially test start-notebook.sh --- tests/base-notebook/test_healthcheck.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/base-notebook/test_healthcheck.py b/tests/base-notebook/test_healthcheck.py index 0e54d30038..50d83c27ad 100644 --- a/tests/base-notebook/test_healthcheck.py +++ b/tests/base-notebook/test_healthcheck.py @@ -22,6 +22,7 @@ (["RESTARTABLE=yes"], None, None), (["JUPYTER_PORT=8171"], None, None), (["JUPYTER_PORT=8117", "DOCKER_STACKS_JUPYTER_CMD=notebook"], None, None), + (None, ["start-notebook.sh"], None), (None, ["start-notebook.py", "--ServerApp.base_url=/test"], None), (None, ["start-notebook.py", "--ServerApp.base_url=/test/"], None), (["GEN_CERT=1"], ["start-notebook.py", "--ServerApp.base_url=/test"], None), From 99410dc394c5471e8b7cf327e80ba94428a2b3ad Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Tue, 17 Oct 2023 16:39:49 +0530 Subject: [PATCH 18/18] Reflow warning docs Co-authored-by: Ayaz Salikhov --- docs/using/selecting.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/using/selecting.md b/docs/using/selecting.md index f77d9abb87..7f763feeae 100644 --- a/docs/using/selecting.md +++ b/docs/using/selecting.md @@ -61,10 +61,10 @@ It contains: - Options for a self-signed HTTPS certificate ```{warning} -`jupyter/base-notebook` also contains `start-notebook.sh` and `start-singleuser.sh` files to maintain backwards -compatibility. External config that explicitly refers to those files should instead update to refer to -`start-notebook.py` and `start-singleuser.py` instead. The shim `.sh` files will be removed at some future -date. +`jupyter/base-notebook` also contains `start-notebook.sh` and `start-singleuser.sh` files to maintain backwards compatibility. +External config that explicitly refers to those files should instead +update to refer to `start-notebook.py` and `start-singleuser.py`. +The shim `.sh` files will be removed at some future date. ``` ### jupyter/minimal-notebook